Skip to content

Commit 7d2c2bd

Browse files
committed
Add loading for some match events
1 parent ad41740 commit 7d2c2bd

File tree

8 files changed

+115
-87
lines changed

8 files changed

+115
-87
lines changed

backend/matching-service/config/rabbitmq.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import amqplib, { Connection } from "amqplib";
22
import dotenv from "dotenv";
33
import { matchUsers } from "../src/utils/mq_utils";
4-
import { MatchItem } from "../src/types/matchTypes";
4+
import { MatchItem } from "../src/handlers/matchHandler";
55

66
dotenv.config();
77

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { io } from "../../server";
2-
import { Match, MatchItem, MatchRequest } from "../types/matchTypes";
32
import { v4 as uuidv4 } from "uuid";
43
import {
54
MATCH_FOUND,
@@ -11,16 +10,40 @@ import {
1110
import { Socket } from "socket.io";
1211
import { sendRabbitMq } from "../../config/rabbitmq";
1312

14-
const matches: Match = {};
15-
export const userSockets: Map<string, Socket> = new Map<string, Socket>();
13+
interface MatchUser {
14+
id: string;
15+
username: string;
16+
profile?: string;
17+
}
18+
19+
export interface MatchRequest {
20+
user: MatchUser;
21+
complexities: string[];
22+
categories: string[];
23+
languages: string[];
24+
timeout: number;
25+
}
26+
27+
export interface MatchItem {
28+
user: MatchUser;
29+
complexities: string[];
30+
categories: string[];
31+
languages: string[];
32+
sentTimestamp: number;
33+
ttlInSecs: number;
34+
}
35+
36+
const matches = new Map<string, boolean>();
37+
const userSockets = new Map<string, Socket>();
1638

1739
export const createMatchItem = async (
1840
socket: Socket,
19-
matchRequest: MatchRequest
41+
matchRequest: MatchRequest,
42+
rematch?: boolean
2043
): Promise<boolean> => {
2144
const { user, complexities, categories, languages, timeout } = matchRequest;
2245

23-
if (userSockets.has(user.id)) {
46+
if (!rematch && userSockets.has(user.id)) {
2447
console.log(`user request exists: ${user.username}`);
2548
socket.emit(MATCH_IN_PROGRESS);
2649
return false;
@@ -35,7 +58,6 @@ export const createMatchItem = async (
3558
languages: languages,
3659
sentTimestamp: Date.now(),
3760
ttlInSecs: timeout,
38-
acceptedMatch: false,
3961
};
4062

4163
const result = await sendRabbitMq(matchQueueItem);
@@ -47,14 +69,8 @@ export const createMatchItem = async (
4769

4870
export const createMatch = (matchItem1: MatchItem, matchItem2: MatchItem) => {
4971
const matchId = uuidv4();
50-
matches[matchId] = {
51-
item1: matchItem1,
52-
item2: matchItem2,
53-
timeout: null,
54-
accepted: false,
55-
};
72+
matches.set(matchId, false);
5673

57-
// check for disconnection? or just send the match (disconnected user will timeout anyway)
5874
userSockets.get(matchItem1.user.id)!.join(matchId);
5975
userSockets.get(matchItem2.user.id)!.join(matchId);
6076
io.to(matchId).emit(MATCH_FOUND, {
@@ -65,30 +81,28 @@ export const createMatch = (matchItem1: MatchItem, matchItem2: MatchItem) => {
6581
};
6682

6783
export const handleMatchAcceptance = (matchId: string) => {
68-
const match = matches[matchId];
84+
const match = matches.has(matchId);
6985
if (!match) {
7086
return;
7187
}
7288

73-
if (match.accepted) {
89+
if (matches.get(matchId)) {
7490
io.to(matchId).emit(MATCH_SUCCESSFUL);
7591
} else {
76-
match.accepted = true;
92+
matches.set(matchId, true);
7793
}
7894
};
7995

80-
export const handleRematch = (
96+
export const handleRematch = async (
8197
socket: Socket,
8298
matchId: string,
8399
rematchRequest: MatchRequest
84-
) => {
85-
const match = matches[matchId];
86-
if (match) {
87-
delete matches[matchId];
88-
socket.to(matchId).emit(MATCH_UNSUCCESSFUL);
100+
): Promise<boolean> => {
101+
if (matches.delete(matchId)) {
102+
socket.to(matchId).emit(MATCH_UNSUCCESSFUL); // TODO: emit other user reject?
89103
}
90104

91-
createMatchItem(socket, rematchRequest);
105+
return await createMatchItem(socket, rematchRequest, true);
92106
};
93107

94108
export const handleMatchTermination = (terminatedSocket: Socket) => {
@@ -100,10 +114,14 @@ export const handleMatchTermination = (terminatedSocket: Socket) => {
100114
}
101115

102116
// TODO: no access to rooms
103-
const matchId = Array.from(terminatedSocket.rooms)[1];
104-
const match = matches[matchId];
105-
if (match) {
106-
delete matches[matchId];
107-
terminatedSocket.to(matchId).emit(MATCH_UNSUCCESSFUL);
108-
}
117+
// const matchId = Array.from(terminatedSocket.rooms)[1];
118+
// const match = matches[matchId];
119+
// if (match) {
120+
// delete matches[matchId];
121+
// terminatedSocket.to(matchId).emit(MATCH_UNSUCCESSFUL);
122+
// }
123+
};
124+
125+
export const hasUserDisconnected = (uid: string) => {
126+
return !userSockets.has(uid);
109127
};

backend/matching-service/src/handlers/websocketHandler.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import {
66
SOCKET_CLIENT_DISCONNECT,
77
SOCKET_DISCONNECT,
88
} from "../utils/constants";
9-
import { MatchRequest } from "../types/matchTypes";
109
import {
1110
createMatchItem,
1211
handleMatchAcceptance,
1312
handleMatchTermination,
1413
handleRematch,
14+
MatchRequest,
1515
} from "./matchHandler";
1616

1717
export const handleWebsocketMatchEvents = (socket: Socket) => {
@@ -27,10 +27,25 @@ export const handleWebsocketMatchEvents = (socket: Socket) => {
2727
handleMatchAcceptance(matchId)
2828
);
2929

30-
socket.on(REMATCH_REQUEST, (matchId: string, rematchRequest: MatchRequest) =>
31-
handleRematch(socket, matchId, rematchRequest)
30+
socket.on(
31+
REMATCH_REQUEST,
32+
async (
33+
matchId: string,
34+
rematchRequest: MatchRequest,
35+
callback: (result: boolean) => void
36+
) => {
37+
const result = await handleRematch(socket, matchId, rematchRequest);
38+
callback(result);
39+
}
3240
);
3341

42+
socket.on(SOCKET_DISCONNECT, (reason) => {
43+
if (reason === SOCKET_CLIENT_DISCONNECT) {
44+
console.log("Client manually disconnected");
45+
handleMatchTermination(socket);
46+
}
47+
});
48+
3449
// TODO: handle client reconnect failure
3550
socket.on(SOCKET_DISCONNECT, (reason) => {
3651
if (reason === SOCKET_CLIENT_DISCONNECT) {

backend/matching-service/src/types/matchTypes.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

backend/matching-service/src/utils/mq_utils.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import { createMatch, userSockets } from "../handlers/matchHandler";
2-
import { MatchItem } from "../types/matchTypes";
1+
import {
2+
createMatch,
3+
hasUserDisconnected,
4+
MatchItem,
5+
} from "../handlers/matchHandler";
36

47
const matchingRequests = new Map<string, MatchItem>();
58

@@ -9,13 +12,13 @@ export const matchUsers = (newRequest: string) => {
912
for (const [uid, pendingRequest] of matchingRequests) {
1013
if (
1114
isExpired(pendingRequest) ||
12-
!userSockets.has(uid) ||
15+
hasUserDisconnected(uid) ||
1316
uid === newRequestUid
1417
) {
1518
matchingRequests.delete(uid);
1619
continue;
1720
}
18-
if (isExpired(newRequestJson) || !userSockets.has(newRequestUid)) {
21+
if (isExpired(newRequestJson) || hasUserDisconnected(newRequestUid)) {
1922
return;
2023
}
2124

frontend/src/contexts/MatchContext.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type MatchContextType = {
5050
matchUser: MatchUser | null;
5151
matchCriteria: MatchCriteria;
5252
partner: MatchUser | null;
53+
loading: boolean;
5354
};
5455

5556
const MatchContext = createContext<MatchContextType | null>(null);
@@ -81,6 +82,7 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
8182
});
8283
const [matchId, setMatchId] = useState<string | null>(null);
8384
const [partner, setPartner] = useState<MatchUser | null>(null);
85+
const [loading, setLoading] = useState<boolean>(false);
8486

8587
const closeConnection = () => {
8688
matchSocket.removeAllListeners();
@@ -154,6 +156,7 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
154156
languages: string[],
155157
timeout: number
156158
) => {
159+
setLoading(true);
157160
openConnection();
158161
matchSocket.emit(
159162
MatchEvents.MATCH_REQUEST,
@@ -165,6 +168,7 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
165168
timeout: timeout,
166169
},
167170
(result: boolean) => {
171+
setTimeout(() => setLoading(false), 500);
168172
if (result) {
169173
setMatchCriteria({
170174
complexities,
@@ -192,18 +196,28 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
192196
};
193197

194198
const rematch = () => {
199+
setLoading(true);
200+
setMatchId(null);
201+
setPartner(null);
202+
195203
const rematchRequest = {
196204
user: matchUser,
197205
complexities: matchCriteria.complexities,
198206
categories: matchCriteria.categories,
199207
languages: matchCriteria.languages,
200208
timeout: matchCriteria.timeout,
201209
};
202-
matchSocket.emit(MatchEvents.REMATCH_REQUEST, matchId, rematchRequest);
203-
204-
setMatchId(null);
205-
setPartner(null);
206-
appNavigate("/matching");
210+
matchSocket.emit(
211+
MatchEvents.REMATCH_REQUEST,
212+
matchId,
213+
rematchRequest,
214+
(result: boolean) => {
215+
setTimeout(() => setLoading(false), 500);
216+
if (result) {
217+
appNavigate("/matching");
218+
}
219+
}
220+
);
207221
};
208222

209223
const stopMatch = (path: string) => {
@@ -230,6 +244,7 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
230244
matchUser,
231245
matchCriteria,
232246
partner,
247+
loading,
233248
}}
234249
>
235250
{children}

frontend/src/pages/Home/index.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,30 @@ import reducer, {
2626
import CustomChip from "../../components/CustomChip";
2727
import homepageImage from "/homepage_image.svg";
2828
import { useMatch } from "../../contexts/MatchContext";
29+
import Loader from "../../components/Loader";
2930

3031
const Home: React.FC = () => {
3132
const [complexities, setComplexities] = useState<string[]>([]);
3233
const [categories, setCategories] = useState<string[]>([]);
3334
const [languages, setLanguages] = useState<string[]>([]);
34-
const [timeout, setTimeout] = useState<number>(30);
35+
const [timeout, setTimeout] = useState<number | undefined>(30);
3536

3637
const [state, dispatch] = useReducer(reducer, initialState);
3738

3839
const match = useMatch();
3940
if (!match) {
4041
throw new Error(USE_MATCH_ERROR_MESSAGE);
4142
}
42-
const { findMatch } = match;
43+
const { findMatch, loading } = match;
4344

4445
useEffect(() => {
4546
getQuestionCategories(dispatch);
4647
}, []);
4748

49+
if (loading) {
50+
return <Loader />;
51+
}
52+
4853
return (
4954
<AppMargin
5055
classname={`${classes.fullheight} ${classes.center} ${classes.margins}`}
@@ -230,17 +235,16 @@ const Home: React.FC = () => {
230235
required
231236
fullWidth
232237
type="number"
233-
value={timeout}
238+
onKeyDown={(event) => event.key === "e" && event.preventDefault()}
239+
value={timeout !== undefined ? timeout : ""}
234240
onChange={(event) => {
235-
const value = parseInt(event.target.value, 10);
236-
setTimeout(value);
237-
}}
238-
InputProps={{
239-
inputProps: { min: minMatchTimeout, max: maxMatchTimeout },
241+
const value = event.target.value;
242+
const newTimeout = value ? parseInt(value, 10) : undefined;
243+
setTimeout(newTimeout);
240244
}}
241245
helperText={`Set a timeout between ${minMatchTimeout} to ${maxMatchTimeout} seconds`}
242246
error={
243-
isNaN(timeout) ||
247+
!timeout ||
244248
timeout < minMatchTimeout ||
245249
timeout > maxMatchTimeout
246250
}
@@ -262,15 +266,15 @@ const Home: React.FC = () => {
262266
fullWidth
263267
sx={{ marginTop: 2 }}
264268
// disabled={
265-
// isNaN(timeout) ||
269+
// !timeout ||
266270
// timeout < minMatchTimeout ||
267271
// timeout > maxMatchTimeout ||
268272
// complexities.length == 0 ||
269273
// categories.length == 0 ||
270274
// languages.length == 0
271275
// }
272276
onClick={() =>
273-
findMatch(complexities, categories, languages, timeout)
277+
findMatch(complexities, categories, languages, timeout!)
274278
}
275279
>
276280
Find my match!

0 commit comments

Comments
 (0)