Skip to content

Commit 97d221a

Browse files
committed
PEER-232: Add working WS impl
Signed-off-by: SeeuSim <[email protected]>
1 parent 7377db7 commit 97d221a

File tree

11 files changed

+183
-108
lines changed

11 files changed

+183
-108
lines changed

backend/matching/src/workers/matcher.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,9 @@ async function processMatch(
7272
// Notify both sockets
7373
const { ...matchItems } = getMatchItems({ userId1: requestorUserId, userId2: matchedUserId });
7474

75-
const sendMatchLogic = (socketPort: string) => {
76-
sendNotif([socketPort], MATCHING_EVENT.SUCCESS, matchItems);
77-
sendNotif([socketPort], MATCHING_EVENT.DISCONNECT);
78-
};
79-
80-
// TODO: If client disconnected, stop sending to requestor
81-
sendMatchLogic(matchedSocketPort);
82-
setTimeout(() => {
83-
sendMatchLogic(requestorSocketPort);
84-
}, 1000);
75+
sendNotif([matchedSocketPort, requestorSocketPort], MATCHING_EVENT.SUCCESS, matchItems);
76+
sendNotif([matchedSocketPort, requestorSocketPort], MATCHING_EVENT.DISCONNECT);
77+
8578
await logQueueStatus(logger, redisClient, `Queue Status After Matching: <PLACEHOLDER>`);
8679
return true;
8780
}

backend/matching/src/ws/handlers.ts

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { logger } from '@/lib/utils';
55
import { queueingService } from '@/services';
66
import type { IRedisClient, IRequestMatchEvent } from '@/types';
77

8-
import { MATCHING_EVENT,WS_EVENT } from './events';
8+
import { MATCHING_EVENT, WS_EVENT } from './events';
99

1010
type ISocketIOServer<T> = Server<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, T>;
1111
type ISocketIOSocket<T> = Socket<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, T>;
@@ -56,31 +56,37 @@ export const queueEventHandler =
5656
(!payload.topic && !payload.difficulty) ||
5757
(payload.topic && !Array.isArray(payload.topic))
5858
) {
59-
socket.emit(MATCHING_EVENT.ERROR, `Payload for ${WS_EVENT.START_QUEUING} is invalid.`);
59+
const message = `Payload for ${WS_EVENT.START_QUEUING} is invalid.`;
60+
logger.warn(message);
61+
socket.emit(MATCHING_EVENT.ERROR, message);
6062
return;
6163
}
6264

6365
// 3. Start Queuing
64-
if (!redisClient || !redisClient.isOpen || !redisClient.isReady) {
65-
try {
66+
try {
67+
if (!redisClient || !redisClient.isOpen || !redisClient.isReady) {
6668
redisClient = await client.connect();
67-
const { userId, difficulty, topic } = payload;
68-
const timestamp = `${Date.now()}`;
69-
await queueingService(redisClient, {
70-
userId,
71-
difficulty,
72-
topic,
73-
socketPort: roomId,
74-
timestamp,
75-
});
76-
} catch (error) {
77-
const { name, message, stack, cause } = error as Error;
78-
logger.error({ name, message, stack, cause }, `An error occurred connecting to the client`);
79-
socket.emit(MATCHING_EVENT.ERROR, 'Error connecting to client');
80-
return;
8169
}
82-
}
8370

84-
socket.emit(MATCHING_EVENT.QUEUED);
85-
logQueueStatus(logger, redisClient, `Queue Status Before Matching: <PLACEHOLDER>`);
71+
const { userId, difficulty, topic } = payload;
72+
const timestamp = `${Date.now()}`;
73+
await queueingService(redisClient, {
74+
userId,
75+
difficulty,
76+
topic,
77+
socketPort: roomId,
78+
timestamp,
79+
});
80+
socket.emit(MATCHING_EVENT.QUEUED);
81+
await logQueueStatus(
82+
logger,
83+
redisClient,
84+
`[ws::queueEventHandler] Queue Status Before Matching: <PLACEHOLDER>`
85+
);
86+
} catch (error) {
87+
const { name, message, stack, cause } = error as Error;
88+
logger.error({ name, message, stack, cause }, `An error occurred.`);
89+
socket.emit(MATCHING_EVENT.ERROR, 'Error connecting to client');
90+
return;
91+
}
8692
};

frontend/src/lib/ws/events.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export const WS_EVENT = {
2+
CONNECT: 'connect',
3+
MESSAGE: 'message',
4+
JOIN_ROOM: 'joinRoom',
5+
CANCEL_ROOM: 'cancelRoom',
6+
LEAVE_ROOM: 'leave',
7+
START_QUEUING: 'startQueuing',
8+
DISCONNECT: 'disconnect',
9+
} as const;
10+
11+
export const MATCHING_EVENT = {
12+
ERROR: 'ERROR', // When match encounters error
13+
QUEUED: 'QUEUED', // When match joins pool
14+
MATCHING: 'MATCHING', // When matching in progress
15+
PENDING: 'PENDING', // When waiting for match
16+
SUCCESS: 'SUCCESS', // When match successful
17+
FAILED: 'FAILED', // When match failed
18+
DISCONNECT: 'DISCONNECT', // To disconnect all sockets in room
19+
} as const;

frontend/src/routes/match/logic.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { z } from 'zod';
77

88
import { requestMatch } from '@/services/match-service';
99
import { fetchTopics } from '@/services/question-service';
10+
import { getUserId } from '@/services/user-service';
1011

1112
export interface MatchFormData {
1213
selectedTopics: string[];
@@ -32,10 +33,14 @@ const formSchema = z.object({
3233
difficulty: z.string().min(1, 'Difficulty cannot be empty'),
3334
});
3435

35-
type IRequestMatchFormSchema = z.infer<typeof formSchema>;
36+
export type IRequestMatchFormSchema = z.infer<typeof formSchema>;
3637

3738
export const useRequestMatchForm = () => {
39+
// TODO: Set on RouteGuard is-authed request and then query Context API
40+
const userId = getUserId() as string;
41+
3842
const [socketPort, setSocketPort] = useState('');
43+
3944
const form = useForm<IRequestMatchFormSchema>({
4045
resolver: zodResolver(formSchema),
4146
defaultValues: {
@@ -45,10 +50,11 @@ export const useRequestMatchForm = () => {
4550
mode: 'onSubmit',
4651
});
4752
const { selectedTopics: topic, difficulty } = form.watch();
53+
4854
const { mutate, error, isPending, isSuccess } = useMutation({
4955
mutationKey: ['requestMatch', topic, difficulty],
50-
mutationFn: (data: IRequestMatchFormSchema) => {
51-
return requestMatch(data);
56+
mutationFn: () => {
57+
return requestMatch({ userId });
5258
},
5359
onSuccess(data, _variables, _context) {
5460
if (data && data.socketPort) {
@@ -60,8 +66,8 @@ export const useRequestMatchForm = () => {
6066
},
6167
});
6268

63-
const onSubmit = (data: IRequestMatchFormSchema) => {
64-
mutate(data);
69+
const onSubmit = (_data: IRequestMatchFormSchema) => {
70+
mutate();
6571
};
6672

6773
return {

frontend/src/routes/match/match-form.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
SelectValue,
2121
} from '@/components/ui/select';
2222
import { cn } from '@/lib/utils';
23+
import { MatchRequestStoreProvider } from '@/stores/match-request-store';
2324

2425
import { useRequestMatchForm } from './logic';
2526
import { WaitingRoomModal } from './waiting-room';
@@ -125,7 +126,9 @@ export const MatchForm = ({ topics }: MatchFormProps) => {
125126
</FormProvider>
126127
</CardContent>
127128
</Card>
128-
<WaitingRoomModal {...{ socketPort, isModalOpen, setIsModalOpen }} />
129+
<MatchRequestStoreProvider value={{ form }}>
130+
<WaitingRoomModal {...{ socketPort, isModalOpen, setIsModalOpen, form }} />
131+
</MatchRequestStoreProvider>
129132
</div>
130133
);
131134
};
Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,24 @@
11
import { CircleCheckBig, CircleX, LoaderCircle } from 'lucide-react';
22

3-
export const SOCKET_EVENTS = {
4-
CONNECT: 'connect',
5-
MESSAGE: 'message',
6-
JOIN_ROOM: 'joinRoom',
7-
MATCHING: 'MATCHING',
8-
PENDING: 'PENDING',
9-
SUCCESS: 'SUCCESS',
10-
FAILED: 'FAILED',
11-
};
12-
13-
export const FAILED_STATUS = {
14-
header: 'No match found',
15-
icon: <CircleX color='red' />,
16-
description: 'Match failed.',
17-
};
18-
19-
export const WAITING_STATUS = {
20-
header: 'Waiting for a Partner...',
21-
icon: <LoaderCircle className='animate-spin' />,
22-
description: 'Connecting...',
23-
};
24-
25-
export const CANCELLING_STATUS = {
26-
header: 'Cancelling Match....',
27-
icon: <LoaderCircle className='animate-spin' />,
28-
description: 'Loading...',
29-
};
30-
31-
export const SUCCESS_STATUS = {
32-
header: 'Found a Match!',
33-
icon: <CircleCheckBig color='green' />,
34-
description: 'Match Details:',
3+
export const UI_STATUS = {
4+
FAILED_STATUS: {
5+
header: 'No match found',
6+
icon: <CircleX color='red' />,
7+
description: 'Match failed.',
8+
},
9+
WAITING_STATUS: {
10+
header: 'Waiting for a Partner...',
11+
icon: <LoaderCircle className='animate-spin' />,
12+
description: 'Connecting...',
13+
},
14+
CANCELLING_STATUS: {
15+
header: 'Cancelling Match....',
16+
icon: <LoaderCircle className='animate-spin' />,
17+
description: 'Loading...',
18+
},
19+
SUCCESS_STATUS: {
20+
header: 'Found a Match!',
21+
icon: <CircleCheckBig color='green' />,
22+
description: 'Match Details:',
23+
},
3524
};

frontend/src/routes/match/waiting-room/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
2-
import { Dispatch, FC, SetStateAction } from 'react';
2+
import type { Dispatch, FC, SetStateAction } from 'react';
33

44
import {
55
Dialog,

0 commit comments

Comments
 (0)