Skip to content

Commit 2557048

Browse files
committed
Move websocket logic into services file, add states for finding match and timeout
1 parent dbafdf7 commit 2557048

File tree

4 files changed

+139
-55
lines changed

4 files changed

+139
-55
lines changed

apps/frontend/src/app/match/page.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@ import "./styles.scss";
99
import { useContext } from "react";
1010
import { WebSocketContext } from "@/contexts/websocketcontext";
1111

12-
export default function ProfilePage(): JSX.Element {
12+
export default function MatchPage(): JSX.Element {
1313
const matcher = useContext(WebSocketContext)! // ! is a null-check
14+
1415
let button;
1516
switch (matcher.state) {
1617
case "closed":
17-
button = <Button onClick={matcher.start}>Start Match</Button>
18+
button = <Button onClick={() => {
19+
matcher.start({
20+
"type": "match_request",
21+
"topics": ["Algorithms", "Arrays"],
22+
"difficulties": ["Easy", "Medium"]
23+
})
24+
}}>Start Match</Button>
1825
break;
1926

2027
case "matching":
@@ -26,9 +33,13 @@ export default function ProfilePage(): JSX.Element {
2633
button = <Button loading>{matcher.state}</Button>
2734
break;
2835

29-
// case "ready":
30-
// button = <Button disabled>Ok</Button>
31-
// break;
36+
case "found":
37+
button = <Button onClick={matcher.ok}>Your Partner is {matcher.info.partnerName}</Button>
38+
break;
39+
40+
case "timeout":
41+
button = <Button onClick={matcher.ok}>Timered Out</Button>
42+
break;
3243
}
3344

3445

@@ -37,8 +48,7 @@ export default function ProfilePage(): JSX.Element {
3748
<Header selectedKey={["0"]} />
3849
<Content className="content">
3950
{button}
40-
{"found" in matcher && <Button onClick={matcher.ok}>Found: {matcher.found}</Button>}
41-
<p>{matcher.state.toUpperCase()}</p>
51+
<p>{matcher.state}</p>
4252
</Content>
4353
</Layout>
4454
)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { MatchState } from "@/contexts/websocketcontext";
2+
import { useEffect, useState } from "react";
3+
import useWebSocket, { Options, ReadyState } from "react-use-websocket";
4+
5+
const MATCHING_SERVICE_URL = process.env.NEXT_PUBLIC_MATCHING_SERVICE_URL;
6+
7+
if (MATCHING_SERVICE_URL == undefined) {
8+
throw "NEXT_PUBLIC_MATCHING_SERVICE_URL was not defined in .env";
9+
}
10+
11+
export type MatchRequestParams = {
12+
type: "match_request",
13+
topics: string[],
14+
difficulties: string[],
15+
}
16+
17+
export type MatchFoundResponse = {
18+
type: "match_found",
19+
matchId: number,
20+
partnerId: number,
21+
partnerName: string,
22+
}
23+
24+
export type MatchTimeoutResponse = {
25+
type: "timeout",
26+
message: string,
27+
}
28+
29+
type MatchResponse = MatchFoundResponse | MatchTimeoutResponse;
30+
31+
export default function useMatching(): MatchState {
32+
const [isSocket, setIsSocket] = useState<boolean>(false);
33+
const [ste, setSte] = useState<MatchState>({
34+
state: "closed",
35+
start,
36+
});
37+
38+
const options: Options = {
39+
onClose() {
40+
setIsSocket(false);
41+
},
42+
onMessage({data: response}) {
43+
const responseJson: MatchResponse = JSON.parse(response);
44+
if (responseJson.type == "timeout") {
45+
setIsSocket(false);
46+
setSte({
47+
state: "timeout",
48+
ok: cancel,
49+
})
50+
return;
51+
}
52+
53+
if (responseJson.type == "match_found") {
54+
setIsSocket(false);
55+
setSte({
56+
state: "found",
57+
info: {
58+
matchId: responseJson.matchId.toString(),
59+
partnerId: responseJson.partnerId.toString(),
60+
partnerName: responseJson.partnerName,
61+
},
62+
ok: cancel
63+
})
64+
return;
65+
}
66+
}
67+
}
68+
69+
const {
70+
readyState: socketState,
71+
sendJsonMessage,
72+
} = useWebSocket<MatchResponse>(MATCHING_SERVICE_URL as string, options, isSocket);
73+
74+
function cancel() {
75+
setIsSocket(false)
76+
setSte({
77+
state: "closed",
78+
start,
79+
})
80+
}
81+
function start(request: MatchRequestParams) {
82+
setIsSocket(true)
83+
sendJsonMessage(request);
84+
}
85+
86+
let matchState: MatchState;
87+
switch (socketState) {
88+
case ReadyState.CLOSED:
89+
case ReadyState.UNINSTANTIATED:
90+
matchState = {state: "closed", start}
91+
break;
92+
case ReadyState.OPEN:
93+
matchState = {state: "matching", cancel}
94+
break;
95+
case ReadyState.CONNECTING:
96+
matchState = {state: "starting"}
97+
break;
98+
case ReadyState.CLOSING:
99+
matchState = {state: "cancelling"}
100+
break;
101+
}
102+
103+
return isSocket ? matchState : ste;
104+
}
Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,18 @@
11
"use client"
22

3-
import { ReactNode, useEffect, useState } from "react"
4-
import { type FoundState, type SocketState, WebSocketContext } from "@/contexts/websocketcontext";
5-
import useWebSocket, { ReadyState } from "react-use-websocket"
3+
import { ReactNode } from "react"
4+
import { WebSocketContext } from "@/contexts/websocketcontext";
5+
import useMatching from "@/app/services/use-matching";
66
const MATCHING_SERVICE_URL = process.env.NEXT_PUBLIC_MATCHING_SERVICE_URL;
77

88
if (MATCHING_SERVICE_URL == undefined) {
99
throw "NEXT_PUBLIC_MATCHING_SERVICE_URL was not defined in .env";
1010
}
1111

1212
export default function WebSocketProvider({children}: {children: ReactNode}) {
13-
const [open, setOpen] = useState(false)
14-
const [found, setFound] = useState<FoundState>({})
15-
const { readyState: socketState, lastMessage, sendMessage } = useWebSocket(MATCHING_SERVICE_URL as string, {}, open);
16-
17-
function cancel() {
18-
setOpen(false)
19-
}
20-
function start() {
21-
setOpen(true)
22-
sendMessage("do match");
23-
}
24-
function ok() {
25-
setFound({});
26-
}
27-
28-
// Acts as a message event hook
29-
useEffect(() => {
30-
if (lastMessage == null) {
31-
return;
32-
}
33-
setFound({found: lastMessage.data, ok })
34-
}, [lastMessage])
35-
36-
let matchState: SocketState;
37-
switch (socketState) {
38-
case ReadyState.CLOSED:
39-
case ReadyState.UNINSTANTIATED:
40-
matchState = {state: "closed", start}
41-
break;
42-
case ReadyState.OPEN:
43-
matchState = {state: "matching", cancel}
44-
break;
45-
case ReadyState.CONNECTING:
46-
matchState = {state: "starting"}
47-
break;
48-
case ReadyState.CLOSING:
49-
matchState = {state: "cancelling"}
50-
break;
51-
}
13+
const matchState = useMatching();
5214

53-
return <WebSocketContext.Provider value={{...found, ...matchState}}>
15+
return <WebSocketContext.Provider value={matchState}>
5416
{children}
5517
</WebSocketContext.Provider>
5618
}
Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
1+
import { MatchRequestParams } from "@/app/services/use-matching";
12
import { createContext } from "react";
23

4+
35
export type SocketState = {
46
state: "cancelling" | "starting"
57
} | {
68
state: "closed";
7-
start(): void;
9+
start(req: MatchRequestParams): void;
810
} | {
911
state: "matching";
1012
cancel(): void;
1113
};
1214

13-
export type FoundState = {} | {
14-
found: string;
15+
export type MatchState = SocketState | {
16+
state: "found";
17+
info: {
18+
matchId: string;
19+
partnerId: string;
20+
partnerName: string;
21+
};
22+
ok(): void;
23+
} | {
24+
state: "timeout";
1525
ok(): void;
1626
};
1727

18-
type MatchState = SocketState & FoundState;
19-
2028
export const WebSocketContext = createContext<MatchState | null>(null);
2129

0 commit comments

Comments
 (0)