Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feedback #1

Open
wants to merge 44 commits into
base: feedback
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8671177
Setting up GitHub Classroom Feedback
github-classroom[bot] Aug 29, 2022
ac52c51
add gitignore for nodemodules and .env (#2)
tanwayne890 Sep 5, 2022
1ef2de9
[FR2.1] and setup for [FR2.2] (#4)
tanwayne890 Sep 5, 2022
6bd27b5
Matching feature FR2.2-2.5 (#7)
oliviajohansen Sep 9, 2022
c88f4bf
Add GitHub workflow (#8)
oliviajohansen Sep 9, 2022
e22ae64
FR 1.2 - 1.3 and NFR 1 (#6)
hopinxian Sep 11, 2022
a2790fd
Set up redux store and save user in redux (#15)
oliviajohansen Sep 12, 2022
c67aa56
Delete user API (#16)
TianYong-Goh Sep 22, 2022
c4b1dc5
Reset password API (#17)
TianYong-Goh Sep 23, 2022
630eee8
Change sqlite to mongodb (#18)
tanwayne890 Sep 29, 2022
e635b59
Frontend (#21)
TianYong-Goh Sep 30, 2022
a469861
Fix difficulty selected not being handled correctly (#27)
oliviajohansen Oct 1, 2022
69f82c0
Collaboration service (#23)
oliviajohansen Oct 1, 2022
4b81185
Logout user (#25)
TianYong-Goh Oct 8, 2022
789c83d
Delete user (#32)
TianYong-Goh Oct 8, 2022
fc9c40b
Reset password (#35)
TianYong-Goh Oct 8, 2022
e287309
Add question service (#34)
tanwayne890 Oct 11, 2022
34da7ce
Add Communication service (#33)
hopinxian Oct 11, 2022
2222102
Refactor Frontend And Add Protected Routing (#38)
TianYong-Goh Oct 11, 2022
1269cd5
Matching and collab service sockets and handle edge cases (#37)
oliviajohansen Oct 12, 2022
31b5a4e
QoL Update: run all services in the same terminal (#36)
hopinxian Oct 20, 2022
b571e94
Update with npm i and remove userinfo api (#41)
hopinxian Oct 20, 2022
3bd9c70
Add backend for history-service (#44)
tanwayne890 Oct 27, 2022
a1eec94
Set up deployment (#42)
hopinxian Oct 28, 2022
e7fa3b2
[Backend] Enhancement on history-service and question-service (#50)
tanwayne890 Nov 4, 2022
3402c4d
Fix voice socket not disconnecting (#45)
hopinxian Nov 4, 2022
e236c99
Fix routing (#51)
TianYong-Goh Nov 4, 2022
5aff3d6
Some collab improvements (#46)
oliviajohansen Nov 4, 2022
e8f2119
Add frontend for history and fix matching (#49)
TianYong-Goh Nov 4, 2022
65a517e
Update app yaml (#52)
hopinxian Nov 4, 2022
e2ea3c3
Add viewing logs to README
hopinxian Nov 4, 2022
f8b3b0b
Remove check for duplicate user in collab model (#53)
oliviajohansen Nov 4, 2022
bbb12b3
add redis to question service (#54)
TianYong-Goh Nov 4, 2022
66f3c2d
Remove specific path for matching service (#58)
oliviajohansen Nov 6, 2022
eced4dc
Change React Quill to Quill (#57)
oliviajohansen Nov 6, 2022
1c249b8
Add testing for communication and collaboration service (#56)
hopinxian Nov 7, 2022
3bbfd1c
Use Yjs for crdt (#59)
oliviajohansen Nov 7, 2022
34d73d1
improve ui and resolve newline omitted problem (#55)
TianYong-Goh Nov 7, 2022
b0e2be4
add user service api test (#60)
TianYong-Goh Nov 8, 2022
c3afc33
Update socket test (#62)
hopinxian Nov 8, 2022
904c8e7
[Test] Add tests for matching, history and question service (#61)
tanwayne890 Nov 8, 2022
b6cb01d
Update frontend (#63)
TianYong-Goh Nov 9, 2022
240f537
Uncomment send email function
hopinxian Nov 9, 2022
77b6efc
Update readme and env.sample (#64)
oliviajohansen Nov 9, 2022
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
Prev Previous commit
Next Next commit
Matching feature FR2.2-2.5 (#7)
* add gitignore for nodemodules and .env

* init sqlite database, init matchmodel, create CRUD routes

* add difficultySelection component (radio buttons and submit button)

* add npm uuid for generating userId

* post insert diff

* format typescript

* git ignore dev instance

* allowNull false userId

* install deps

* Set up matching and navigation to empty room

* Prepare room and clean up code

* Set up matching dialog and leave room option

* Clean up

Co-authored-by: Wayne Tan <[email protected]>
  • Loading branch information
oliviajohansen and tanwayne890 authored Sep 9, 2022
commit 6bd27b520bd5302070f8c59a7da064387568d940
6 changes: 3 additions & 3 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 34 additions & 16 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
import SignupPage from './components/SignupPage';
import DifficultySelection from './components/DifficultySelection';
import {
BrowserRouter as Router,
Routes,
Route,
Navigate,
} from "react-router-dom";
import SignupPage from "./components/SignupPage";
import DifficultySelection from "./components/DifficultySelection";
import Room from "./components/Room";
import { Box } from "@mui/material";
import { URL_MATCH_SVC } from "./configs";
import io from "socket.io-client";

const socket = io.connect(URL_MATCH_SVC);

function App() {
return (
<div className="App">
<Box display={"flex"} flexDirection={"column"} padding={"4rem"}>
<Router>
<Routes>
<Route exact path="/" element={<Navigate replace to="/signup" />}></Route>
<Route path="/signup" element={<SignupPage />} />
<Route path="/diff" element={<DifficultySelection />} />
</Routes>
</Router>
</Box>
</div>
);
return (
<div className="App">
<Box display={"flex"} flexDirection={"column"} padding={"4rem"}>
<Router>
<Routes>
<Route
exact
path="/"
element={<Navigate replace to="/signup" />}
></Route>
<Route path="/signup" element={<SignupPage />} />
<Route
path="/diff"
element={<DifficultySelection socket={socket} />}
/>
<Route path="/room/:id" element={<Room socket={socket} />} />
</Routes>
</Router>
</Box>
</div>
);
}

export default App;
188 changes: 125 additions & 63 deletions frontend/src/components/DifficultySelection.js
Original file line number Diff line number Diff line change
@@ -1,90 +1,152 @@
import React from "react";
import React, { useState, useEffect } from "react";
import axios from "axios";
import { URL_INSERT_DIFFICULTY } from "../configs";
import { STATUS_CODE_CREATED } from "../constants";
import { v4 as uuidv4 } from "uuid";
import { useNavigate } from "react-router-dom";
import MatchingDialog from "./MatchingDialog";

class DifficultySelection extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedDifficulty: "easy",
userId: uuidv4(), // random uuid
};
}
export const MatchStatus = {
NOT_MATCHING: "NOT_MATCHING",
MATCHING: "MATCHING",
MATCH_SUCCESS: "MATCH_SUCCESS",
MATH_FAILED: "MATCH_FAILED",
};

handleOptionChange = (event) => {
this.setState({
selectedDifficulty: event.target.value,
function DifficultySelection({ socket }) {
const [selectedDifficulty, setSelectedDifficulty] = useState(null);
const [userId, setUserId] = useState(uuidv4()); // random uuid
const [matchStatus, setMatchStatus] = useState(MatchStatus.NOT_MATCHING);
const [isMatchingDialogOpen, setIsMatchingDialogOpen] = useState(false);
const navigate = useNavigate();

useEffect(() => {
socket.on("match_success", (data) => {
successFindingMatch();
const roomId = data.roomId;
navigate(`/room/${roomId}`);
socket.emit("join_room", roomId);
socket.emit("prep_room", data);
});
return () => socket.off("match_success");
}, [socket]);

const startFindingMatch = () => {
setMatchStatus(MatchStatus.MATCHING);
setIsMatchingDialogOpen(true);
socket.emit("find_match", {
userId: userId,
difficulty: selectedDifficulty,
});
};

const failedFindingMatch = () => {
setMatchStatus(MatchStatus.MATH_FAILED);
socket.emit("stop_find_match", {
userId: userId,
difficulty: selectedDifficulty,
});
};

handleFormSubmit = async (event) => {
const stopFindingMatch = () => {
setIsMatchingDialogOpen(false);
if (matchStatus !== MatchStatus.MATH_FAILED) {
setMatchStatus(MatchStatus.NOT_MATCHING);
socket.emit("stop_find_match", {
userId: userId,
difficulty: selectedDifficulty,
});
}
};

const successFindingMatch = () => {
setMatchStatus(MatchStatus.MATCH_SUCCESS);
setIsMatchingDialogOpen(false);
};

const handleOptionChange = (event) => {
setSelectedDifficulty(event.target.value);
};

const handleFormSubmit = async (event) => {
event.preventDefault();
console.log("Your userId: ", this.state.userId);
console.log("You have selected: ", this.state.selectedDifficulty);
if (!selectedDifficulty) {
console.log("You must select a difficulty level before matching");
return;
}
console.log("Your userId: ", userId);
console.log("You have selected: ", selectedDifficulty);

// Post difficult level chosen.
const res = await axios
.post(URL_INSERT_DIFFICULTY, {
userId: this.state.userId,
difficulty: this.state.selectedDifficulty,
userId: userId,
difficulty: selectedDifficulty,
})
.catch(() => {
console.log("Please try again later");
});
if (res && res.status === STATUS_CODE_CREATED) {
console.log("Difficulty successfully inserted");
}

// Start finding match for the user.
startFindingMatch();
};

render() {
return (
<div className="container">
<div className="row">
<div className="col-sm-12">
<form onSubmit={this.handleFormSubmit}>
<div className="radio">
<label>
<input
type="radio"
value="easy"
checked={this.state.selectedDifficulty === "easy"}
onChange={this.handleOptionChange}
/>
Easy
</label>
</div>
<div className="radio">
<label>
<input
type="radio"
value="medium"
checked={this.state.selectedDifficulty === "medium"}
onChange={this.handleOptionChange}
/>
Medium
</label>
</div>
<div className="radio">
<label>
<input
type="radio"
value="hard"
checked={this.state.selectedDifficulty === "hard"}
onChange={this.handleOptionChange}
/>
Hard
</label>
</div>
<button className="btn btn-default" type="submit">
Match
</button>
</form>
</div>
return (
<div className="container">
<MatchingDialog
initSeconds={30}
isOpen={isMatchingDialogOpen}
handleClose={stopFindingMatch}
matchStatus={matchStatus}
failedFindingMatch={failedFindingMatch}
/>
<div className="row">
<div className="col-sm-12">
<form onSubmit={handleFormSubmit}>
<div className="radio">
<label>
<input
type="radio"
value="easy"
checked={selectedDifficulty === "easy"}
onChange={handleOptionChange}
/>
Easy
</label>
</div>
<div className="radio">
<label>
<input
type="radio"
value="medium"
checked={selectedDifficulty === "medium"}
onChange={handleOptionChange}
/>
Medium
</label>
</div>
<div className="radio">
<label>
<input
type="radio"
value="hard"
checked={selectedDifficulty === "hard"}
onChange={handleOptionChange}
/>
Hard
</label>
</div>
<button className="btn btn-default" type="submit">
Match
</button>
</form>
</div>
</div>
);
}
</div>
);
}

export default DifficultySelection;
95 changes: 95 additions & 0 deletions frontend/src/components/MatchingDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState, useEffect } from "react";
import { Modal, Box, Typography, Button } from "@mui/material";
import { MatchStatus } from "./DifficultySelection";

function MatchingDialog({
initSeconds,
isOpen,
handleClose,
matchStatus,
failedFindingMatch,
}) {
const [seconds, setSeconds] = useState(initSeconds);
let title = "";
let description = "";

useEffect(() => {
if (matchStatus === MatchStatus.MATCHING) {
let myInterval = setInterval(() => {
if (seconds > 0) {
setSeconds(seconds - 1);
}
if (seconds === 0) {
clearInterval(myInterval);
failedFindingMatch();
}
}, 1000);
return () => {
clearInterval(myInterval);
};
}
});

useEffect(() => {
if (!isOpen) {
return;
}
setSeconds(30);
}, [isOpen]);

switch (matchStatus) {
case MatchStatus.MATCHING:
title = "Finding a match...";
description = "Please wait.";
break;
case MatchStatus.NOT_MATCHING:
title = "Matching cancelled.";
description = "";
break;
case MatchStatus.MATCH_SUCCESS:
title = "Found a match!";
description = "";
break;
case MatchStatus.MATH_FAILED:
title = "Failed to find a match.";
description = "Please try again later.";
break;
default:
console.log("Invalid matchStatus supplied to MatchingDialog");
}

const style = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
border: "2px solid #000",
boxShadow: 24,
p: 4,
};

return (
<Modal
open={isOpen}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Typography id="modal-modal-title" variant="h6" component="h2">
{title}
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
{description} Timer: {seconds} seconds
</Typography>
<Button variant="outlined" onClick={handleClose}>
Cancel
</Button>
</Box>
</Modal>
);
}

export default MatchingDialog;
Loading