Skip to content

Commit

Permalink
Merge pull request #59 from CS3219-AY2425S1/frontend-changes2
Browse files Browse the repository at this point in the history
Frontend changes2
  • Loading branch information
bensohh authored Nov 4, 2024
2 parents 48d6745 + 3ff07c4 commit f77bbfa
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 353 deletions.
309 changes: 170 additions & 139 deletions apps/frontend/src/app/matching/MatchingModal.tsx
Original file line number Diff line number Diff line change
@@ -1,154 +1,185 @@
import React, { useState, useEffect } from "react";
import { Modal } from "antd";
import "typeface-montserrat";
import "./styles.scss";
import FindMatchContent from "./modalContent/FindMatchContent";
import MatchingInProgressContent from "./modalContent/MatchingInProgressContent";
import MatchFoundContent from "./modalContent/MatchFoundContent";
import JoinedMatchContent from "./modalContent/JoinedMatchContent";
import MatchNotFoundContent from "./modalContent/MatchNotFoundContent";
import MatchCancelledContent from "./modalContent/MatchCancelledContent";
import useMatching from "../services/use-matching";
import { useRouter } from "next/navigation";
"use client"

import React, { useState, useEffect } from 'react';
import {
Form,
Button,
Modal,
} from 'antd';
import 'typeface-montserrat';
import './styles.scss';
import FindMatchContent from './modalContent/FindMatchContent';
import MatchingInProgressContent from './modalContent/MatchingInProgressContent';
import MatchFoundContent from './modalContent/MatchFoundContent';
import JoinedMatchContent from './modalContent/JoinedMatchContent';
import MatchNotFoundContent from './modalContent/MatchNotFoundContent';
import MatchCancelledContent from './modalContent/MatchCancelledContent';
import useMatching from '../services/use-matching';
import { ValidateUser } from '../services/user';
import { useTimer } from 'react-timer-hook';
import { useRouter } from 'next/navigation';

interface MatchingModalProps {
isOpen: boolean;
close: () => void;
}

const MatchingModal: React.FC<MatchingModalProps> = ({
isOpen,
close: _close,
}) => {
const router = useRouter();
const matchingState = useMatching();
const [closedType, setClosedType] = useState<
"finding" | "cancelled" | "joined"
>("finding");
const [timeoutAfter, setTimeoutAfter] = useState<number>(9999);
const isClosable = ["timeout", "closed"].includes(matchingState.state);
export interface MatchParams {
topics: string[],
difficulties: string[],
}
const MATCH_TIMEOUT = 30;
const JOIN_TIMEOUT = 5;

function close() {
// clean up matching and closedType State
if (matchingState.state === "timeout") {
matchingState.ok();
}
setClosedType("finding");
_close();
}
const MatchingModal: React.FC<MatchingModalProps> = ({ isOpen, close: _close }) => {
const matchingState = useMatching();
const [closedType, setClosedType] = useState<"finding" | "cancelled" | "joined">("finding");
const isClosable = ["timeout", "closed"].includes(matchingState.state) && closedType != "joined";
const router = useRouter();
const { totalSeconds, pause: pauseTimer, restart: restartTimer } = useTimer({
expiryTimestamp: new Date(Date.now() + MATCH_TIMEOUT * 1000),
autoStart: false,
onExpire() {
if (matchingState.state === "matching") {
matchingState.timeout();
return;
}
if (matchingState.state === "found") {
join();
return;
}
console.warn(`matching is in ${matchingState.state}`)
},
});
const passed = MATCH_TIMEOUT - totalSeconds;

const renderModalContent = () => {
switch (matchingState.state) {
case "closed":
switch (closedType) {
case "finding":
return <FindMatchContent beginMatch={matchingState.start} />;
case "cancelled":
return (
<MatchCancelledContent
reselect={() => {
setClosedType("finding");
}}
retry={() => {}}
canceledIn={timeoutAfter}
/>
);
case "joined":
return (
<JoinedMatchContent
cancel={() => {
setClosedType("cancelled");
}}
name1={matchingState.info?.user || ""}
name2={matchingState.info?.matchedUser || ""}
/>
);
function close() {
// clean up matching and closedType State
if (matchingState.state === "timeout") {
matchingState.ok();
}
case "matching":
return (
<MatchingInProgressContent
cancelMatch={(timeoutAfter: number) => {
setClosedType("cancelled");
setTimeoutAfter(timeoutAfter);
matchingState.cancel();
}}
timeout={(timeoutAfter: number) => {
matchingState.timeout();
setTimeoutAfter(timeoutAfter);
}}
/>
);
case "cancelling":
return (
<MatchingInProgressContent
cancelMatch={() => {}}
timeout={() => {}}
/>
);
case "starting":
return <FindMatchContent beginMatch={() => {}} />;
case "found":
return (
<MatchFoundContent
cancel={() => {
matchingState.ok();
setClosedType("cancelled");
}}
join={() => {
matchingState.ok();
setClosedType("joined");
localStorage.setItem("user", matchingState.info.user);
localStorage.setItem(
"matchedUser",
matchingState.info.matchedUser
);
localStorage.setItem("collabId", matchingState.info.matchId);
localStorage.setItem(
"questionDocRefId",
matchingState.info.questionDocRefId
);
localStorage.setItem(
"matchedTopics",
matchingState.info.matchedTopics.join(",")
);
setClosedType("finding");
_close();
}

// Redirect to collaboration page
router.push(`/collaboration/${matchingState.info.matchId}`);
}}
name1={matchingState.info.user}
name2={matchingState.info.matchedUser}
/>
const startMatch = matchingState.state == "closed" || matchingState.state == "timeout" ? async (params: MatchParams): Promise<void> => {
const user = await ValidateUser();

restartTimer(
new Date(Date.now() + MATCH_TIMEOUT * 1000),
);
case "timeout":
return (
<MatchNotFoundContent
reselect={matchingState.ok}
retry={() => {}}
timedOutIn={10}
/>

matchingState.start({
email: user.data.email,
username: user.data.username,
type: "match_request",
...params
});
} : undefined;

const join = matchingState.state == "found" ? (() => {
matchingState.ok();
setClosedType("joined");
localStorage.setItem("user", matchingState.info.user);
localStorage.setItem(
"matchedUser",
matchingState.info.matchedUser
);
default:
throw new Error("Invalid matching state.");
}
};
localStorage.setItem("collabId", matchingState.info.matchId);
localStorage.setItem("questionDocRefId", matchingState.info.questionDocRefId);
localStorage.setItem("matchedTopics", matchingState.info.matchedTopics.join(","));

// Redirect to collaboration page
router.push(`/collaboration/${matchingState.info.matchId}`);
}) : () => { throw new Error("join called when not found"); }

useEffect(() => {
if (matchingState.state === "cancelling" || matchingState.state === "timeout") {
pauseTimer();
return;
}
if (matchingState.state === "found") {
restartTimer(
new Date(Date.now() + JOIN_TIMEOUT * 1000),
)
}
}, [matchingState])

return (
<Modal
open={isOpen}
onCancel={close}
footer={null}
closable={false}
maskClosable={false}
className="modal"
>
{renderModalContent()}
{isClosable && (
<button className="close-button" onClick={close}>
Close
</button>
)}
</Modal>
);
};
const renderModalContent = () => {
switch (matchingState.state) {
case 'closed':
switch (closedType) {
case "finding":
return <FindMatchContent beginMatch={params => {}}/>;
case "cancelled":
return <MatchCancelledContent
reselect={() => {
setClosedType("finding");
}}
canceledIn={passed}
/>;
case "joined":
return <JoinedMatchContent
cancel={() => {
setClosedType("cancelled");
}}
name1={matchingState.info?.user ?? ""}
name2={matchingState.info?.matchedUser ?? ""}
/>;
}
case 'matching':
return <MatchingInProgressContent
cancelMatch={() => {
setClosedType("cancelled");
matchingState.cancel();
pauseTimer();
}}
timePassed={passed}
/>;
case 'cancelling':
return <MatchingInProgressContent cancelMatch={() => {}} timePassed={passed}/>;
case 'starting':
return <FindMatchContent beginMatch={() => {}}/>
case 'found':
return <MatchFoundContent
cancel={() => {
matchingState.ok();
setClosedType("cancelled");
}}
join={join}
name1={matchingState.info.user}
name2={matchingState.info.matchedUser}
joiningIn={totalSeconds}
/>
case 'timeout':
return <MatchNotFoundContent reselect={matchingState.ok} timedOutIn={passed}/>;
default:
throw new Error('Invalid matching state.');
}
}

return (
<Modal open={isOpen}
onCancel={close}
footer={null}
closable={false}
maskClosable={false}
className="modal"
>
<Form<MatchParams>
name="match"
onFinish={startMatch}
initialValues={{
topics: [],
difficulties: [],
}}>
{renderModalContent()}
</Form>
{isClosable && (
<button className="close-button" onClick={close}>Close</button>
)}
</Modal>
)
}

export default MatchingModal;
Loading

0 comments on commit f77bbfa

Please sign in to comment.