Skip to content

Commit dff9969

Browse files
authored
Merge pull request #125 from ruiqi7/bugfix/general
Fix user authentication for socket connections
2 parents 5927898 + 71b883a commit dff9969

File tree

13 files changed

+541
-447
lines changed

13 files changed

+541
-447
lines changed

frontend/src/components/Chat/index.tsx

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
import { Box, styled, TextField, Typography } from "@mui/material";
22
import { useEffect, useRef, useState } from "react";
3-
import {
4-
CommunicationEvents,
5-
communicationSocket,
6-
} from "../../utils/communicationSocket";
7-
import { useMatch } from "../../contexts/MatchContext";
8-
import {
9-
USE_AUTH_ERROR_MESSAGE,
10-
USE_MATCH_ERROR_MESSAGE,
11-
} from "../../utils/constants";
12-
import { useAuth } from "../../contexts/AuthContext";
3+
import { CommunicationEvents } from "../../utils/communicationSocket";
4+
import { USE_COLLAB_ERROR_MESSAGE } from "../../utils/constants";
135
import { toast } from "react-toastify";
6+
import { useCollab } from "../../contexts/CollabContext";
147

158
type Message = {
169
from: string;
@@ -35,32 +28,26 @@ const StyledTypography = styled(Typography)(({ theme }) => ({
3528
const Chat: React.FC<ChatProps> = ({ isActive, setHasNewMessage }) => {
3629
const [messages, setMessages] = useState<Message[]>([]);
3730
const [inputValue, setInputValue] = useState("");
38-
const match = useMatch();
39-
const auth = useAuth();
31+
4032
const messagesRef = useRef<HTMLDivElement>(null);
4133
const errorHandledRef = useRef(false);
4234

43-
if (!match) {
44-
throw new Error(USE_MATCH_ERROR_MESSAGE);
45-
}
46-
47-
if (!auth) {
48-
throw new Error(USE_AUTH_ERROR_MESSAGE);
35+
const collab = useCollab();
36+
if (!collab) {
37+
throw new Error(USE_COLLAB_ERROR_MESSAGE);
4938
}
50-
51-
const { getMatchId } = match;
52-
const { user } = auth;
39+
const { communicationSocket, collabUser, roomId } = collab;
5340

5441
useEffect(() => {
5542
// join the room automatically when this loads
56-
communicationSocket.open();
57-
communicationSocket.emit(CommunicationEvents.JOIN, {
58-
roomId: getMatchId(),
59-
username: user?.username,
43+
communicationSocket?.open();
44+
communicationSocket?.emit(CommunicationEvents.JOIN, {
45+
roomId: roomId,
46+
username: collabUser?.username,
6047
});
6148

6249
return () => {
63-
communicationSocket.emit(CommunicationEvents.USER_DISCONNECT);
50+
communicationSocket?.emit(CommunicationEvents.USER_DISCONNECT);
6451
};
6552
// eslint-disable-next-line react-hooks/exhaustive-deps
6653
}, []);
@@ -78,19 +65,25 @@ const Chat: React.FC<ChatProps> = ({ isActive, setHasNewMessage }) => {
7865
}
7966
};
8067

81-
communicationSocket.on(CommunicationEvents.USER_JOINED, listener);
82-
communicationSocket.on(CommunicationEvents.TEXT_MESSAGE_RECEIVED, listener);
83-
communicationSocket.on(CommunicationEvents.DISCONNECTED, listener);
84-
communicationSocket.on(CommunicationEvents.CONNECT_ERROR, errorListener);
68+
communicationSocket?.on(CommunicationEvents.USER_JOINED, listener);
69+
communicationSocket?.on(
70+
CommunicationEvents.TEXT_MESSAGE_RECEIVED,
71+
listener
72+
);
73+
communicationSocket?.on(CommunicationEvents.DISCONNECTED, listener);
74+
communicationSocket?.on(CommunicationEvents.CONNECT_ERROR, errorListener);
8575

8676
return () => {
87-
communicationSocket.off(CommunicationEvents.USER_JOINED, listener);
88-
communicationSocket.off(
77+
communicationSocket?.off(CommunicationEvents.USER_JOINED, listener);
78+
communicationSocket?.off(
8979
CommunicationEvents.TEXT_MESSAGE_RECEIVED,
9080
listener
9181
);
92-
communicationSocket.off(CommunicationEvents.DISCONNECTED, listener);
93-
communicationSocket.off(CommunicationEvents.CONNECT_ERROR, errorListener);
82+
communicationSocket?.off(CommunicationEvents.DISCONNECTED, listener);
83+
communicationSocket?.off(
84+
CommunicationEvents.CONNECT_ERROR,
85+
errorListener
86+
);
9487
};
9588
// eslint-disable-next-line react-hooks/exhaustive-deps
9689
}, []);
@@ -139,7 +132,7 @@ const Chat: React.FC<ChatProps> = ({ isActive, setHasNewMessage }) => {
139132
{msg.message}
140133
</Typography>
141134
</Box>
142-
) : msg.from === user?.username ? (
135+
) : msg.from === collabUser?.username ? (
143136
<Box
144137
key={id}
145138
sx={(theme) => ({
@@ -187,10 +180,10 @@ const Chat: React.FC<ChatProps> = ({ isActive, setHasNewMessage }) => {
187180
const trimmedValue = inputValue.trim();
188181
if (e.key === "Enter" && !e.shiftKey && trimmedValue !== "") {
189182
e.preventDefault();
190-
communicationSocket.emit(CommunicationEvents.SEND_TEXT_MESSAGE, {
191-
roomId: getMatchId(),
183+
communicationSocket?.emit(CommunicationEvents.SEND_TEXT_MESSAGE, {
184+
roomId: roomId,
192185
message: trimmedValue,
193-
username: user?.username,
186+
username: collabUser?.username,
194187
createdTime: Date.now(),
195188
});
196189
setInputValue("");

frontend/src/components/CodeEditor/index.tsx

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,21 @@ import { EditorView } from "@codemirror/view";
55
import { EditorState } from "@codemirror/state";
66
import { indentUnit } from "@codemirror/language";
77
import { useEffect, useState } from "react";
8-
import { initDocument } from "../../utils/collabSocket";
98
import { cursorExtension } from "../../utils/collabCursor";
109
import { yCollab } from "y-codemirror.next";
1110
import { Doc, Text } from "yjs";
1211
import { Awareness } from "y-protocols/awareness";
1312
import { useCollab } from "../../contexts/CollabContext";
1413
import {
14+
COLLAB_DOCUMENT_INIT_ERROR,
1515
USE_COLLAB_ERROR_MESSAGE,
16-
USE_MATCH_ERROR_MESSAGE,
1716
} from "../../utils/constants";
18-
import { useMatch } from "../../contexts/MatchContext";
17+
import { toast } from "react-toastify";
1918

2019
interface CodeEditorProps {
2120
editorState?: { doc: Doc; text: Text; awareness: Awareness };
22-
uid?: string;
23-
username?: string;
2421
language: string;
2522
template?: string;
26-
roomId?: string;
2723
isReadOnly?: boolean;
2824
}
2925

@@ -34,30 +30,24 @@ const languageSupport = {
3430
};
3531

3632
const CodeEditor: React.FC<CodeEditorProps> = (props) => {
37-
const {
38-
editorState,
39-
uid = "",
40-
username = "",
41-
language,
42-
template = "",
43-
roomId = "",
44-
isReadOnly = false,
45-
} = props;
46-
47-
const match = useMatch();
48-
if (!match) {
49-
throw new Error(USE_MATCH_ERROR_MESSAGE);
50-
}
51-
52-
const { matchCriteria, matchUser, partner, questionId, questionTitle } =
53-
match;
33+
const { editorState, language, template = "", isReadOnly = false } = props;
5434

5535
const collab = useCollab();
5636
if (!collab) {
5737
throw new Error(USE_COLLAB_ERROR_MESSAGE);
5838
}
5939

60-
const { checkDocReady } = collab;
40+
const {
41+
collabUser,
42+
collabPartner,
43+
roomId,
44+
qnId,
45+
qnTitle,
46+
initDocument,
47+
checkDocReady,
48+
sendCursorUpdate,
49+
receiveCursorUpdate,
50+
} = collab;
6151

6252
const [isEditorReady, setIsEditorReady] = useState<boolean>(false);
6353
const [isDocumentLoaded, setIsDocumentLoaded] = useState<boolean>(false);
@@ -74,25 +64,24 @@ const CodeEditor: React.FC<CodeEditorProps> = (props) => {
7464
}
7565

7666
const loadTemplate = async () => {
77-
if (
78-
matchUser &&
79-
partner &&
80-
matchCriteria &&
81-
questionId &&
82-
questionTitle
83-
) {
67+
if (collabUser && collabPartner && roomId && qnId && qnTitle) {
8468
checkDocReady(roomId, editorState.doc, setIsDocumentLoaded);
85-
await initDocument(
86-
uid,
87-
roomId,
88-
template,
89-
matchUser.id,
90-
partner.id,
91-
matchCriteria.language,
92-
questionId,
93-
questionTitle
94-
);
95-
setIsDocumentLoaded(true);
69+
try {
70+
await initDocument(
71+
roomId,
72+
template,
73+
collabUser.id,
74+
collabPartner.id,
75+
language,
76+
qnId,
77+
qnTitle
78+
);
79+
setIsDocumentLoaded(true);
80+
} catch {
81+
toast.error(COLLAB_DOCUMENT_INIT_ERROR);
82+
}
83+
} else {
84+
toast.error(COLLAB_DOCUMENT_INIT_ERROR);
9685
}
9786
};
9887
loadTemplate();
@@ -111,10 +100,16 @@ const CodeEditor: React.FC<CodeEditorProps> = (props) => {
111100
indentUnit.of("\t"),
112101
basicSetup(),
113102
languageSupport[language as keyof typeof languageSupport],
114-
...(!isReadOnly && editorState
103+
...(!isReadOnly && editorState && roomId && collabUser
115104
? [
116105
yCollab(editorState.text, editorState.awareness),
117-
cursorExtension(roomId, uid, username),
106+
cursorExtension(
107+
roomId,
108+
collabUser.id,
109+
collabUser.username,
110+
sendCursorUpdate,
111+
receiveCursorUpdate
112+
),
118113
]
119114
: []),
120115
EditorView.lineWrapping,

frontend/src/components/CollabSessionControls/index.tsx

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,36 @@ import {
55
COLLAB_ENDED_MESSAGE,
66
COLLAB_PARTNER_DISCONNECTED_MESSAGE,
77
USE_COLLAB_ERROR_MESSAGE,
8-
USE_MATCH_ERROR_MESSAGE,
98
} from "../../utils/constants";
109
import { useEffect, useReducer, useRef, useState } from "react";
1110
import CustomDialog from "../CustomDialog";
1211
import {
1312
extractMinutesFromTime,
1413
extractSecondsFromTime,
1514
} from "../../utils/sessionTime";
16-
import { CollabEvents, collabSocket } from "../../utils/collabSocket";
1715
import { toast } from "react-toastify";
1816
import reducer, {
1917
getQuestionById,
2018
initialState,
2119
} from "../../reducers/questionReducer";
22-
import { useMatch } from "../../contexts/MatchContext";
20+
import { CollabEvents } from "../../utils/collabSocket";
2321

2422
const CollabSessionControls: React.FC = () => {
25-
const match = useMatch();
26-
if (!match) {
27-
throw new Error(USE_MATCH_ERROR_MESSAGE);
28-
}
29-
30-
const { questionId } = match;
31-
3223
const collab = useCollab();
3324
if (!collab) {
3425
throw new Error(USE_COLLAB_ERROR_MESSAGE);
3526
}
3627

3728
const {
29+
collabSocket,
3830
handleSubmitSessionClick,
3931
handleEndSessionClick,
4032
handleConfirmEndSession,
41-
isEndSessionModalOpen,
4233
handleRejectEndSession,
4334
handleExitSession,
35+
isEndSessionModalOpen,
4436
isExitSessionModalOpen,
37+
qnId,
4538
qnHistoryId,
4639
stopTime,
4740
setStopTime,
@@ -54,21 +47,21 @@ const CollabSessionControls: React.FC = () => {
5447
const { selectedQuestion } = state;
5548

5649
useEffect(() => {
57-
collabSocket.once(CollabEvents.END_SESSION, (sessionDuration: number) => {
50+
collabSocket?.once(CollabEvents.END_SESSION, (sessionDuration: number) => {
5851
collabSocket.off(CollabEvents.PARTNER_DISCONNECTED);
5952
toast.info(COLLAB_ENDED_MESSAGE);
6053
handleConfirmEndSession(timeRef.current, setTime, true, sessionDuration);
6154
});
6255

63-
collabSocket.once(CollabEvents.PARTNER_DISCONNECTED, () => {
56+
collabSocket?.once(CollabEvents.PARTNER_DISCONNECTED, () => {
6457
collabSocket.off(CollabEvents.END_SESSION);
6558
toast.error(COLLAB_PARTNER_DISCONNECTED_MESSAGE);
6659
handleConfirmEndSession(timeRef.current, setTime, true);
6760
});
6861

6962
return () => {
70-
collabSocket.off(CollabEvents.END_SESSION);
71-
collabSocket.off(CollabEvents.PARTNER_DISCONNECTED);
63+
collabSocket?.off(CollabEvents.END_SESSION);
64+
collabSocket?.off(CollabEvents.PARTNER_DISCONNECTED);
7265
};
7366
// eslint-disable-next-line react-hooks/exhaustive-deps
7467
}, []);
@@ -96,11 +89,11 @@ const CollabSessionControls: React.FC = () => {
9689
}, [qnHistoryId]);
9790

9891
useEffect(() => {
99-
if (!questionId) {
92+
if (!qnId) {
10093
return;
10194
}
102-
getQuestionById(questionId, dispatch);
103-
}, [questionId]);
95+
getQuestionById(qnId, dispatch);
96+
}, [qnId]);
10497

10598
return (
10699
<Stack direction={"row"} alignItems={"center"} spacing={2}>

frontend/src/components/Navbar/Navbar.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ beforeEach(() => {
2727
retryMatch: jest.fn(),
2828
matchingTimeout: jest.fn(),
2929
matchOfferTimeout: jest.fn(),
30+
matchId: null,
3031
matchUser: null,
3132
matchCriteria: null,
3233
partner: null,

frontend/src/components/Navbar/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
} from "@mui/material";
1515
import { grey } from "@mui/material/colors";
1616
import AppMargin from "../AppMargin";
17-
import { useNavigate, useLocation } from "react-router-dom";
17+
import { useNavigate, useLocation, Link as RouterLink } from "react-router-dom";
1818
import { useAuth } from "../../contexts/AuthContext";
1919
import { useState } from "react";
2020
import {
@@ -93,8 +93,9 @@ const Navbar: React.FC<NavbarProps> = (props) => {
9393
.filter((item) => !item.needsLogin || (item.needsLogin && user))
9494
.map((item) => (
9595
<Link
96+
component={RouterLink}
97+
to={item.link}
9698
key={item.label}
97-
href={item.link}
9899
underline="none"
99100
sx={{ color: "common.black" }}
100101
>

0 commit comments

Comments
 (0)