Skip to content

Commit 1b02dd9

Browse files
committed
Hook up new logic for matching to frontend
Note 2 things: 1. A bug related to incomplete cancellation has surfaced, and become more prominent as a user can now match themselves. -- Suggested fix is to both send a cancellation signal and to do a paranoid check on the User ID attached to the match queue - though paranoid check might not be necessary because RabbitMQ ensures ordering is kept. 2. Collab Service has yet to implement their side of the change fully - in particular, no persistence is performed yet, so reconnection is still a problem.
1 parent a4d07a6 commit 1b02dd9

File tree

8 files changed

+44
-30
lines changed

8 files changed

+44
-30
lines changed

peerprep/api/structs.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ export interface MatchRequest {
5555
requestTime: string;
5656
}
5757

58+
export interface MatchReqInitRes {
59+
match_code: string;
60+
}
61+
5862
export interface MatchData {
5963
roomId: string;
6064
user1: string;

peerprep/app/api/internal/matching/helper.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ import {
33
MatchRequest,
44
MatchResponse,
55
StatusBody,
6+
MatchReqInitRes
67
} from "@/api/structs";
78

89
// helper to be called from client to check storage blob
910
export async function checkMatchStatus(
10-
userId: string
11+
matchHash: string
1112
): Promise<MatchResponse | StatusBody> {
12-
console.debug("In matching helper, checking storage blob:", userId);
13+
console.debug("In matching helper, checking storage blob:", matchHash);
1314
const res = await fetch(
14-
`${process.env.NEXT_PUBLIC_NGINX}/api/internal/matching?uid=${userId}`,
15+
`${process.env.NEXT_PUBLIC_NGINX}/api/internal/matching?matchHash=${matchHash}`,
1516
{
1617
method: "GET",
1718
}
@@ -33,7 +34,7 @@ export async function checkMatchStatus(
3334

3435
export async function findMatch(
3536
matchRequest: MatchRequest
36-
): Promise<StatusBody> {
37+
): Promise<StatusBody|MatchReqInitRes> {
3738
console.debug(
3839
"In matching helper, posting match request",
3940
JSON.stringify(matchRequest)
@@ -49,8 +50,8 @@ export async function findMatch(
4950
return {
5051
error: await res.text(),
5152
status: res.status,
52-
};
53+
} as StatusBody;
5354
}
5455
const json = await res.json();
55-
return json as StatusBody;
56+
return json as MatchReqInitRes;
5657
}

peerprep/app/api/internal/matching/route.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import { NextRequest, NextResponse } from "next/server";
44

55
// all get request interpreted as getting from storage blob
66
export async function GET(request: NextRequest) {
7-
const uid = request.nextUrl.searchParams.get("uid"); // Assuming you're passing the userId as a query parameter
8-
console.log("in route,", uid);
9-
if (!uid) {
10-
return NextResponse.json({ error: "User ID is required" }, { status: 400 });
7+
const matchHash = request.nextUrl.searchParams.get("matchHash"); // Assuming you're passing the userId as a query parameter
8+
console.log("in route,", matchHash);
9+
if (!matchHash) {
10+
return NextResponse.json({ error: "MatchHash is required" }, { status: 400 });
1111
}
1212

1313
try {
1414
const response = await fetch(
15-
`${process.env.NEXT_PUBLIC_STORAGE_BLOB}/request/${uid}`,
15+
`${process.env.NEXT_PUBLIC_STORAGE_BLOB}/request/${matchHash}`,
1616
{
1717
method: "GET",
1818
headers: generateAuthHeaders(),
@@ -50,7 +50,7 @@ export async function POST(request: NextRequest) {
5050
);
5151
if (response.ok) {
5252
return NextResponse.json(
53-
{ status: response.status },
53+
{ match_code: (await response.json()).match_code },
5454
{ status: response.status }
5555
);
5656
}

peerprep/app/questions/[question]/[roomID]/page.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import React from "react";
66
import QuestionBlock from "./question";
77

88
type Props = {
9+
searchParams: {
10+
match?: string
11+
},
912
params: {
1013
question: string;
1114
roomID: string;
1215
};
1316
};
1417

15-
async function Question({ params }: Props) {
18+
async function Question({ params, searchParams }: Props) {
1619
const question = await fetchQuestion(params.question);
1720

1821
return (
@@ -24,6 +27,7 @@ async function Question({ params }: Props) {
2427
question={question as QnType}
2528
roomID={params.roomID}
2629
authToken={getSessionToken()}
30+
matchHash={searchParams.match}
2731
/>
2832
)}
2933
</div>

peerprep/app/questions/[question]/[roomID]/question.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface Props {
1313
question: Question;
1414
roomID?: String;
1515
authToken?: String;
16+
matchHash?: String;
1617
}
1718

1819
interface DifficultyChipProps {
@@ -29,7 +30,7 @@ function DifficultyChip({ diff }: DifficultyChipProps) {
2930
);
3031
}
3132

32-
function QuestionBlock({ question, roomID, authToken }: Props) {
33+
function QuestionBlock({ question, roomID, authToken, matchHash }: Props) {
3334
const router = useRouter();
3435

3536
const handleDelete = async () => {
@@ -95,6 +96,7 @@ function QuestionBlock({ question, roomID, authToken }: Props) {
9596
question={question}
9697
roomID={roomID}
9798
authToken={authToken}
99+
matchHash={matchHash}
98100
/>
99101
</div>
100102
</>

peerprep/components/questionpage/CollabEditor.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ interface Props {
4848
question: Question;
4949
roomID?: String;
5050
authToken?: String;
51+
matchHash?: String;
5152
}
5253

53-
export default function CollabEditor({ question, roomID, authToken }: Props) {
54+
export default function CollabEditor({ question, roomID, authToken, matchHash }: Props) {
5455
const [theme, setTheme] = useState("terminal");
5556
const [fontSize, setFontSize] = useState(18);
5657
const [language, setLanguage] = useState("python");
@@ -86,6 +87,7 @@ export default function CollabEditor({ question, roomID, authToken }: Props) {
8687
const authMessage = {
8788
type: "auth",
8889
token: authToken,
90+
matchHash: matchHash // omitted if undefined
8991
};
9092
newSocket.send(JSON.stringify(authMessage));
9193
};

peerprep/components/questionpage/Matchmaking.tsx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const usePeriodicCallback = (
5555
const Matchmaking = () => {
5656
const router = useRouter();
5757
const [isMatching, setIsMatching] = useState<boolean>(false);
58+
const [matchHash, setMatchHash] = useState<string>("");
5859
const { difficulties, topicList } = useQuestionFilter();
5960
const [difficultyFilter, setDifficultyFilter] = useState<string>(
6061
Difficulty.Easy
@@ -86,12 +87,16 @@ const Matchmaking = () => {
8687
return matchRequest;
8788
};
8889

90+
// TODO: Canceling the match should propagate a cancellation signal
91+
// Currently, we can actually match yourself rn due to this change
92+
// This indicates to me that 1 users can match on a cancellation
8993
const handleMatch = async () => {
9094
if (!isMatching) {
9195
setIsMatching(true);
9296

9397
// start 30s timeout
9498
timeout.current = setTimeout(() => {
99+
setMatchHash("");
95100
setIsMatching(false);
96101
console.log("Match request timed out after 30s");
97102
}, TIMEOUT_MILLISECONDS);
@@ -103,22 +108,25 @@ const Matchmaking = () => {
103108

104109
// send match request
105110
const status = await findMatch(matchRequest);
106-
if (status.error) {
111+
if (isError(status)) {
107112
stopTimer();
108113
console.log("Failed to find match. Cancel matching.");
114+
setMatchHash("");
109115
setIsMatching(false);
110116
return;
111117
}
118+
setMatchHash(status.match_code);
112119
console.log(`Started finding match.`);
113120
} else {
121+
setMatchHash("");
114122
stopTimer();
115123
setIsMatching(false);
116124
console.log("User stopped matching");
117125
}
118126
};
119127

120128
const queryResource = async () => {
121-
const res = await checkMatchStatus(userid);
129+
const res = await checkMatchStatus(matchHash);
122130
if (isError(res)) {
123131
// for now 404 means no match found so dont stop matching on error, let request timeout
124132
return;
@@ -128,17 +136,8 @@ const Matchmaking = () => {
128136
// TODO: iron out what is in a match response and sync up with collab service rooms
129137
const matchRes: MatchResponse = res as MatchResponse;
130138
console.log("Match found!");
131-
// display in a popup for now
132-
// const message = `Room ID: ${matchRes.data.roomId}
133-
// User1: ${matchRes.data.user1}
134-
// User2: ${matchRes.data.user2}
135-
// Question: ${matchRes.data.questionId}
136-
// `;
137-
const message = "Match found! Redirecting to collab page...";
138-
window.alert(message);
139-
// redirect to question page
140139
router.push(
141-
`/questions/${matchRes.data.questionId}/${matchRes.data.roomId}`
140+
`/questions/${matchRes.data.questionId}/${matchRes.data.roomId}?match=${matchHash}`
142141
);
143142
};
144143

@@ -151,8 +150,8 @@ const Matchmaking = () => {
151150
Add Question
152151
</PeerprepButton>
153152
<div className="flex flex-row items-center space-x-4">
154-
<PeerprepButton onClick={handleMatch}>
155-
{isMatching ? "Cancel Match" : "Find Match"}
153+
<PeerprepButton onClick={handleMatch} disabled={!isMatching && matchHash !== ""}>
154+
{isMatching ? "Cancel Match" : matchHash === "" ? "Find Match" : "Redirecting..."}
156155
</PeerprepButton>
157156
{!isMatching && (
158157
<PeerprepDropdown

peerprep/components/shared/PeerprepButton.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ type PeerprepButtonProps = {
66
onClick: () => void;
77
children: React.ReactNode;
88
className?: string;
9+
disabled?: boolean
910
};
1011

1112
const PeerprepButton: React.FC<PeerprepButtonProps> = ({
1213
onClick,
1314
children,
1415
className,
16+
disabled
1517
}) => {
1618
return (
17-
<button onClick={onClick} className={`${styles.button} ${className}`}>
19+
<button onClick={onClick} className={`${styles.button} ${className}`} disabled={disabled}>
1820
{children}
1921
</button>
2022
);

0 commit comments

Comments
 (0)