Skip to content

Commit 8002061

Browse files
committed
Handle websocket close events
1 parent 7d2c2bd commit 8002061

File tree

6 files changed

+167
-49
lines changed

6 files changed

+167
-49
lines changed

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

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import {
44
MATCH_FOUND,
55
MATCH_IN_PROGRESS,
66
MATCH_SUCCESSFUL,
7-
MATCH_UNSUCCESSFUL,
7+
MATCH_ENDED,
88
MATCH_REQUEST_ERROR,
9+
MATCH_NOT_ACCEPTED,
910
} from "../utils/constants";
1011
import { Socket } from "socket.io";
1112
import { sendRabbitMq } from "../../config/rabbitmq";
@@ -16,6 +17,12 @@ interface MatchUser {
1617
profile?: string;
1718
}
1819

20+
interface Match {
21+
uid1: string;
22+
uid2: string;
23+
accepted: boolean;
24+
}
25+
1926
export interface MatchRequest {
2027
user: MatchUser;
2128
complexities: string[];
@@ -33,18 +40,17 @@ export interface MatchItem {
3340
ttlInSecs: number;
3441
}
3542

36-
const matches = new Map<string, boolean>();
43+
const matches = new Map<string, Match>();
3744
const userSockets = new Map<string, Socket>();
3845

3946
export const createMatchItem = async (
4047
socket: Socket,
4148
matchRequest: MatchRequest,
42-
rematch?: boolean
49+
isRematch?: boolean
4350
): Promise<boolean> => {
4451
const { user, complexities, categories, languages, timeout } = matchRequest;
4552

46-
if (!rematch && userSockets.has(user.id)) {
47-
console.log(`user request exists: ${user.username}`);
53+
if (!isRematch && userSockets.has(user.id)) {
4854
socket.emit(MATCH_IN_PROGRESS);
4955
return false;
5056
}
@@ -68,11 +74,18 @@ export const createMatchItem = async (
6874
};
6975

7076
export const createMatch = (matchItem1: MatchItem, matchItem2: MatchItem) => {
77+
const uid1 = matchItem1.user.id;
78+
const uid2 = matchItem2.user.id;
79+
7180
const matchId = uuidv4();
72-
matches.set(matchId, false);
81+
matches.set(matchId, {
82+
uid1: uid1,
83+
uid2: uid2,
84+
accepted: false,
85+
});
7386

74-
userSockets.get(matchItem1.user.id)!.join(matchId);
75-
userSockets.get(matchItem2.user.id)!.join(matchId);
87+
userSockets.get(uid1)?.join(matchId);
88+
userSockets.get(uid2)?.join(matchId);
7689
io.to(matchId).emit(MATCH_FOUND, {
7790
matchId: matchId,
7891
user1: matchItem1.user,
@@ -81,15 +94,21 @@ export const createMatch = (matchItem1: MatchItem, matchItem2: MatchItem) => {
8194
};
8295

8396
export const handleMatchAcceptance = (matchId: string) => {
84-
const match = matches.has(matchId);
97+
const match = matches.get(matchId);
8598
if (!match) {
8699
return;
87100
}
88101

89-
if (matches.get(matchId)) {
102+
if (match.accepted) {
90103
io.to(matchId).emit(MATCH_SUCCESSFUL);
91104
} else {
92-
matches.set(matchId, true);
105+
matches.set(matchId, { ...match, accepted: true });
106+
}
107+
};
108+
109+
const handleMatchDecline = (socket: Socket, matchId: string) => {
110+
if (matches.delete(matchId)) {
111+
socket.to(matchId).emit(MATCH_NOT_ACCEPTED);
93112
}
94113
};
95114

@@ -98,28 +117,45 @@ export const handleRematch = async (
98117
matchId: string,
99118
rematchRequest: MatchRequest
100119
): Promise<boolean> => {
101-
if (matches.delete(matchId)) {
102-
socket.to(matchId).emit(MATCH_UNSUCCESSFUL); // TODO: emit other user reject?
103-
}
104-
120+
handleMatchDecline(socket, matchId);
105121
return await createMatchItem(socket, rematchRequest, true);
106122
};
107123

108-
export const handleMatchTermination = (terminatedSocket: Socket) => {
124+
export const handleMatchStopRequest = (
125+
socket: Socket,
126+
uid: string | undefined,
127+
matchId: string | null,
128+
matchPending: boolean,
129+
isMutual: boolean
130+
) => {
131+
if (matchId) {
132+
if (matchPending) {
133+
handleMatchDecline(socket, matchId);
134+
return;
135+
}
136+
137+
const match = matches.get(matchId);
138+
if (match) {
139+
userSockets.delete(match.uid1);
140+
userSockets.delete(match.uid2);
141+
matches.delete(matchId);
142+
}
143+
144+
if (!isMutual) {
145+
socket.to(matchId).emit(MATCH_ENDED);
146+
}
147+
} else if (uid) {
148+
userSockets.delete(uid);
149+
}
150+
};
151+
152+
export const handleUserDisconnect = (disconnectedSocket: Socket) => {
109153
for (const [uid, socket] of userSockets) {
110-
if (socket.id === terminatedSocket.id) {
154+
if (socket.id === disconnectedSocket.id) {
111155
userSockets.delete(uid);
112156
break;
113157
}
114158
}
115-
116-
// TODO: no access to rooms
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-
// }
123159
};
124160

125161
export const hasUserDisconnected = (uid: string) => {

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import {
55
MATCH_REQUEST,
66
SOCKET_CLIENT_DISCONNECT,
77
SOCKET_DISCONNECT,
8+
MATCH_STOP_REQUEST,
89
} from "../utils/constants";
910
import {
1011
createMatchItem,
1112
handleMatchAcceptance,
12-
handleMatchTermination,
13+
handleMatchStopRequest,
14+
handleUserDisconnect,
1315
handleRematch,
1416
MatchRequest,
1517
} from "./matchHandler";
@@ -39,18 +41,26 @@ export const handleWebsocketMatchEvents = (socket: Socket) => {
3941
}
4042
);
4143

42-
socket.on(SOCKET_DISCONNECT, (reason) => {
43-
if (reason === SOCKET_CLIENT_DISCONNECT) {
44-
console.log("Client manually disconnected");
45-
handleMatchTermination(socket);
44+
socket.on(
45+
MATCH_STOP_REQUEST,
46+
(
47+
uid: string | undefined,
48+
matchId: string | null,
49+
matchPending: boolean,
50+
isMutual: boolean,
51+
callback: () => void
52+
) => {
53+
handleMatchStopRequest(socket, uid, matchId, matchPending, isMutual);
54+
callback();
4655
}
47-
});
56+
);
4857

4958
// TODO: handle client reconnect failure
5059
socket.on(SOCKET_DISCONNECT, (reason) => {
5160
if (reason === SOCKET_CLIENT_DISCONNECT) {
5261
console.log("Client manually disconnected");
53-
handleMatchTermination(socket);
62+
handleUserDisconnect(socket);
5463
}
64+
console.log("disconnect");
5565
});
5666
};

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ export const MATCH_REQUEST_ERROR = "match_request_error";
44
export const MATCH_FOUND = "match_found";
55
export const MATCH_IN_PROGRESS = "match_in_progress";
66
export const MATCH_ACCEPTED = "match_accepted";
7+
export const MATCH_NOT_ACCEPTED = "match_not_accepted";
78
export const REMATCH_REQUEST = "rematch_request";
89
export const MATCH_SUCCESSFUL = "match_successful";
9-
export const MATCH_UNSUCCESSFUL = "match_unsuccessful";
10+
export const MATCH_ENDED = "match_ended";
11+
export const MATCH_STOP_REQUEST = "match_stop_request";
1012

1113
export const SOCKET_DISCONNECT = "disconnect";
1214
export const SOCKET_CLIENT_DISCONNECT = "client namespace disconnect";

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export const matchUsers = (newRequest: string) => {
2323
}
2424

2525
if (isMatch(newRequestJson, pendingRequest)) {
26+
matchingRequests.delete(uid);
2627
createMatch(pendingRequest, newRequestJson);
2728
console.log(`matched ${uid} and ${newRequestUid}`);
2829
return;

frontend/src/contexts/MatchContext.tsx

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ enum MatchEvents {
2626
MATCH_FOUND = "match_found",
2727
MATCH_IN_PROGRESS = "match_in_progress",
2828
MATCH_ACCEPTED = "match_accepted",
29+
MATCH_NOT_ACCEPTED = "match_not_accepted",
2930
REMATCH_REQUEST = "rematch_request",
3031
MATCH_SUCCESSFUL = "match_successful",
31-
MATCH_UNSUCCESSFUL = "match_unsuccessful",
32+
MATCH_ENDED = "match_ended",
33+
MATCH_STOP_REQUEST = "match_stop_request",
3234

3335
SOCKET_DISCONNECT = "disconnect",
3436
SOCKET_CLIENT_DISCONNECT = "io client disconnect",
@@ -46,9 +48,10 @@ type MatchContextType = {
4648
retryMatch: () => void;
4749
acceptMatch: () => void;
4850
rematch: () => void;
49-
stopMatch: (path: string) => void;
51+
stopMatch: (path: string, mutual?: boolean) => void;
5052
matchUser: MatchUser | null;
5153
matchCriteria: MatchCriteria;
54+
matchId: string | null;
5255
partner: MatchUser | null;
5356
loading: boolean;
5457
};
@@ -82,6 +85,7 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
8285
});
8386
const [matchId, setMatchId] = useState<string | null>(null);
8487
const [partner, setPartner] = useState<MatchUser | null>(null);
88+
const [matchPending, setMatchPending] = useState<boolean>(false);
8589
const [loading, setLoading] = useState<boolean>(false);
8690

8791
const closeConnection = () => {
@@ -99,6 +103,7 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
99103
matchSocket.on(MatchEvents.MATCH_FOUND, ({ matchId, user1, user2 }) => {
100104
setMatchId(matchId);
101105
matchUser?.id === user1.id ? setPartner(user2) : setPartner(user1);
106+
setMatchPending(true);
102107
appNavigate("/matching/matched");
103108
});
104109
}
@@ -111,14 +116,23 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
111116

112117
if (!matchSocket.hasListeners(MatchEvents.MATCH_SUCCESSFUL)) {
113118
matchSocket.on(MatchEvents.MATCH_SUCCESSFUL, () => {
119+
setMatchPending(false);
114120
appNavigate("/collaboration");
115121
});
116122
}
117123

118-
if (!matchSocket.hasListeners(MatchEvents.MATCH_UNSUCCESSFUL)) {
119-
matchSocket.on(MatchEvents.MATCH_UNSUCCESSFUL, () => {
120-
toast.error("Matching unsuccessful!");
121-
stopMatch("/home");
124+
if (!matchSocket.hasListeners(MatchEvents.MATCH_NOT_ACCEPTED)) {
125+
matchSocket.on(MatchEvents.MATCH_NOT_ACCEPTED, () => {
126+
toast.error("Unfortunately, your partner did not accept the match.");
127+
setMatchId(null);
128+
setMatchPending(false);
129+
});
130+
}
131+
132+
if (!matchSocket.hasListeners(MatchEvents.MATCH_ENDED)) {
133+
matchSocket.on(MatchEvents.MATCH_ENDED, () => {
134+
toast.error("Your partner has ended the match."); // TODO: stop match during matching
135+
afterMatchCleanUp("/home");
122136
});
123137
}
124138

@@ -199,6 +213,7 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
199213
setLoading(true);
200214
setMatchId(null);
201215
setPartner(null);
216+
setMatchPending(false);
202217

203218
const rematchRequest = {
204219
user: matchUser,
@@ -220,7 +235,18 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
220235
);
221236
};
222237

223-
const stopMatch = (path: string) => {
238+
const stopMatch = (path: string, isMutual: boolean = false) => {
239+
matchSocket.emit(
240+
MatchEvents.MATCH_STOP_REQUEST,
241+
matchUser?.id,
242+
matchId,
243+
matchPending,
244+
isMutual,
245+
() => afterMatchCleanUp(path)
246+
);
247+
};
248+
249+
const afterMatchCleanUp = (path: string) => {
224250
closeConnection();
225251
setMatchCriteria({
226252
complexities: [],
@@ -230,6 +256,7 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
230256
});
231257
setMatchId(null);
232258
setPartner(null);
259+
setMatchPending(false);
233260
appNavigate(path);
234261
};
235262

@@ -243,6 +270,7 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
243270
stopMatch,
244271
matchUser,
245272
matchCriteria,
273+
matchId,
246274
partner,
247275
loading,
248276
}}

0 commit comments

Comments
 (0)