Skip to content

Commit 69e7846

Browse files
authored
Merge pull request #101 from ruiqi7/feature/coding-sandbox
Fix code template bug in editor
2 parents 6329576 + 1356a66 commit 69e7846

File tree

8 files changed

+105
-116
lines changed

8 files changed

+105
-116
lines changed

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

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ enum CollabEvents {
1818
DOCUMENT_READY = "document_ready",
1919
UPDATE = "updateV2",
2020
UPDATE_CURSOR = "update_cursor",
21-
// PARTNER_LEFT = "partner_left",
22-
// PARTNER_DISCONNECTED = "partner_disconnected",
21+
PARTNER_LEFT = "partner_left",
2322
}
2423

2524
const EXPIRY_TIME = 3600;
@@ -92,27 +91,24 @@ export const handleWebsocketCollabEvents = (socket: Socket) => {
9291
}
9392
);
9493

95-
socket.on(CollabEvents.LEAVE, (uid: string, roomId: string) => {
96-
const connectionKey = `${uid}:${roomId}`;
97-
if (!userConnections.has(connectionKey)) {
98-
return;
99-
}
100-
101-
clearTimeout(userConnections.get(connectionKey)!);
94+
socket.on(
95+
CollabEvents.LEAVE,
96+
(uid: string, roomId: string, isImmediate: boolean) => {
97+
const connectionKey = `${uid}:${roomId}`;
98+
if (isImmediate || !userConnections.has(connectionKey)) {
99+
handleUserLeave(uid, roomId, socket);
100+
return;
101+
}
102102

103-
const connectionTimeout = setTimeout(() => {
104-
userConnections.delete(connectionKey);
105-
socket.leave(roomId);
106-
socket.disconnect();
103+
clearTimeout(userConnections.get(connectionKey)!);
107104

108-
const room = io.sockets.adapter.rooms.get(roomId);
109-
if (!room || room.size === 0) {
110-
removeCollabSession(roomId);
111-
}
112-
}, CONNECTION_DELAY);
105+
const connectionTimeout = setTimeout(() => {
106+
handleUserLeave(uid, roomId, socket);
107+
}, CONNECTION_DELAY);
113108

114-
userConnections.set(connectionKey, connectionTimeout);
115-
});
109+
userConnections.set(connectionKey, connectionTimeout);
110+
}
111+
);
116112

117113
socket.on(CollabEvents.RECONNECT_REQUEST, async (roomId: string) => {
118114
// TODO: Handle recconnection
@@ -168,3 +164,21 @@ const saveDocument = async (roomId: string, doc: Doc) => {
168164
EX: EXPIRY_TIME,
169165
});
170166
};
167+
168+
const handleUserLeave = (uid: string, roomId: string, socket: Socket) => {
169+
const connectionKey = `${uid}:${roomId}`;
170+
if (userConnections.has(connectionKey)) {
171+
clearTimeout(userConnections.get(connectionKey)!);
172+
userConnections.delete(connectionKey);
173+
}
174+
175+
socket.leave(roomId);
176+
socket.disconnect();
177+
178+
const room = io.sockets.adapter.rooms.get(roomId);
179+
if (!room || room.size === 0) {
180+
removeCollabSession(roomId);
181+
} else {
182+
io.to(roomId).emit(CollabEvents.PARTNER_LEFT);
183+
}
184+
};

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ enum MatchEvents {
3232
MATCH_FOUND = "match_found",
3333
MATCH_SUCCESSFUL = "match_successful",
3434
MATCH_UNSUCCESSFUL = "match_unsuccessful",
35-
MATCH_ENDED = "match_ended",
3635
MATCH_REQUEST_EXISTS = "match_request_exists",
3736
MATCH_REQUEST_ERROR = "match_request_error",
3837
}
@@ -199,10 +198,7 @@ export const handleWebsocketMatchEvents = (socket: Socket) => {
199198

200199
socket.on(MatchEvents.MATCH_END_REQUEST, (uid: string, matchId: string) => {
201200
userConnections.delete(uid);
202-
const matchDeleted = handleMatchDelete(matchId);
203-
if (matchDeleted) {
204-
socket.to(matchId).emit(MatchEvents.MATCH_ENDED);
205-
}
201+
handleMatchDelete(matchId);
206202
});
207203

208204
socket.on(
@@ -257,7 +253,6 @@ const endMatchOnUserDisconnect = (socket: Socket, uid: string) => {
257253
const matchDeleted = handleMatchDelete(matchId);
258254
if (matchDeleted) {
259255
socket.to(matchId).emit(MatchEvents.MATCH_UNSUCCESSFUL); // on matching page
260-
socket.to(matchId).emit(MatchEvents.MATCH_ENDED); // on collab page
261256
}
262257
}
263258
};

frontend/src/components/CodeEditor/index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const CodeEditor: React.FC<CodeEditorProps> = (props) => {
7676
return (
7777
<CodeMirror
7878
ref={onEditorReady}
79-
style={{ height: "100%", width: "100%" }}
79+
style={{ height: "100%", width: "100%", fontSize: "14px" }}
8080
height="100%"
8181
width="100%"
8282
basicSetup={false}
@@ -96,7 +96,12 @@ const CodeEditor: React.FC<CodeEditorProps> = (props) => {
9696
EditorView.editable.of(!isReadOnly && isDocumentLoaded),
9797
EditorState.readOnly.of(isReadOnly || !isDocumentLoaded),
9898
]}
99-
value={isReadOnly ? template : template ? "Loading code template..." : ""}
99+
value={isReadOnly ? template : undefined}
100+
placeholder={
101+
!isReadOnly && !isDocumentLoaded
102+
? "Loading code template..."
103+
: undefined
104+
}
100105
/>
101106
);
102107
};

frontend/src/contexts/CollabContext.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
FAILED_TESTCASE_MESSAGE,
77
SUCCESS_TESTCASE_MESSAGE,
88
FAILED_TO_SUBMIT_CODE_MESSAGE,
9+
COLLAB_ENDED_MESSAGE,
910
} from "../utils/constants";
1011
import { toast } from "react-toastify";
1112

@@ -14,9 +15,10 @@ import { codeExecutionClient } from "../utils/api";
1415
import { useReducer } from "react";
1516
import { updateQnHistoryById } from "../reducers/qnHistoryReducer";
1617
import qnHistoryReducer, { initialQHState } from "../reducers/qnHistoryReducer";
17-
import { leave } from "../utils/collabSocket";
18+
import { CollabEvents, collabSocket, leave } from "../utils/collabSocket";
1819
import { CommunicationEvents } from "../components/Chat";
1920
import { communicationSocket } from "../utils/communicationSocket";
21+
import useAppNavigate from "../components/UseAppNavigate";
2022

2123
type CompilerResult = {
2224
status: string;
@@ -35,14 +37,17 @@ type CollabContextType = {
3537
handleEndSessionClick: () => void;
3638
handleRejectEndSession: () => void;
3739
handleConfirmEndSession: () => void;
40+
checkPartnerStatus: () => void;
3841
setCode: React.Dispatch<React.SetStateAction<string>>;
3942
compilerResult: CompilerResult[];
43+
isEndSessionModalOpen: boolean;
4044
};
4145

4246
const CollabContext = createContext<CollabContextType | null>(null);
4347

4448
const CollabProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
4549
const { children } = props;
50+
const appNavigate = useAppNavigate();
4651

4752
const match = useMatch();
4853

@@ -58,16 +63,17 @@ const CollabProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
5863
stopMatch,
5964
questionId,
6065
qnHistoryId,
61-
setIsEndSessionModalOpen,
6266
} = match;
6367

6468
// eslint-disable-next-line
65-
const [qnHistoryState, qnHistoryDispatch] = useReducer(
69+
const [_qnHistoryState, qnHistoryDispatch] = useReducer(
6670
qnHistoryReducer,
6771
initialQHState
6872
);
6973
const [code, setCode] = useState<string>("");
7074
const [compilerResult, setCompilerResult] = useState<CompilerResult[]>([]);
75+
const [isEndSessionModalOpen, setIsEndSessionModalOpen] =
76+
useState<boolean>(false);
7177

7278
const handleSubmitSessionClick = async (time: number) => {
7379
try {
@@ -121,8 +127,8 @@ const CollabProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
121127
setIsEndSessionModalOpen(false);
122128

123129
// Leave collaboration room
124-
leave(matchUser?.id as string, getMatchId() as string);
125-
leave(partner?.id as string, getMatchId() as string);
130+
leave(matchUser?.id as string, getMatchId() as string, true);
131+
leave(partner?.id as string, getMatchId() as string, true);
126132

127133
// Leave chat room
128134
communicationSocket.emit(
@@ -136,8 +142,18 @@ const CollabProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
136142
partner?.username
137143
);
138144

139-
// End match
145+
// Delete match data
140146
stopMatch();
147+
appNavigate("/home");
148+
};
149+
150+
const checkPartnerStatus = () => {
151+
collabSocket.on(CollabEvents.PARTNER_LEFT, () => {
152+
toast.error(COLLAB_ENDED_MESSAGE);
153+
setIsEndSessionModalOpen(false);
154+
stopMatch();
155+
appNavigate("/home");
156+
});
141157
};
142158

143159
return (
@@ -147,8 +163,10 @@ const CollabProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
147163
handleEndSessionClick,
148164
handleRejectEndSession,
149165
handleConfirmEndSession,
166+
checkPartnerStatus,
150167
setCode,
151168
compilerResult,
169+
isEndSessionModalOpen,
152170
}}
153171
>
154172
{children}

frontend/src/contexts/MatchContext.tsx

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
ABORT_MATCH_PROCESS_CONFIRMATION_MESSAGE,
77
FAILED_MATCH_REQUEST_MESSAGE,
88
MATCH_CONNECTION_ERROR,
9-
MATCH_ENDED_MESSAGE,
109
MATCH_LOGIN_REQUIRED_MESSAGE,
1110
MATCH_REQUEST_EXISTS_MESSAGE,
1211
MATCH_UNSUCCESSFUL_MESSAGE,
@@ -18,9 +17,6 @@ import useAppNavigate from "../components/UseAppNavigate";
1817
import { UNSAFE_NavigationContext } from "react-router-dom";
1918
import { Action, type History, type Transition } from "history";
2019

21-
import { useReducer } from "react";
22-
import qnHistoryReducer, { initialQHState } from "../reducers/qnHistoryReducer";
23-
2420
let matchUserId: string;
2521
let partnerUserId: string;
2622

@@ -54,7 +50,6 @@ enum MatchEvents {
5450
MATCH_FOUND = "match_found",
5551
MATCH_SUCCESSFUL = "match_successful",
5652
MATCH_UNSUCCESSFUL = "match_unsuccessful",
57-
MATCH_ENDED = "match_ended",
5853
MATCH_REQUEST_EXISTS = "match_request_exists",
5954
MATCH_REQUEST_ERROR = "match_request_error",
6055

@@ -93,8 +88,6 @@ type MatchContextType = {
9388
partner: MatchUser | null;
9489
matchPending: boolean;
9590
loading: boolean;
96-
isEndSessionModalOpen: boolean;
97-
setIsEndSessionModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
9891
questionId: string | null;
9992
qnHistoryId: string | null;
10093
};
@@ -124,15 +117,6 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
124117
const [questionId, setQuestionId] = useState<string | null>(null);
125118
const [qnHistoryId, setQnHistoryId] = useState<string | null>(null);
126119

127-
const [isEndSessionModalOpen, setIsEndSessionModalOpen] =
128-
useState<boolean>(false);
129-
130-
// eslint-disable-next-line
131-
const [qnHistoryState, qnHistoryDispatch] = useReducer(
132-
qnHistoryReducer,
133-
initialQHState
134-
);
135-
136120
const navigator = useContext(UNSAFE_NavigationContext).navigator as History;
137121

138122
useEffect(() => {
@@ -233,9 +217,6 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
233217
case MatchPaths.MATCHED:
234218
initMatchedListeners();
235219
return;
236-
case MatchPaths.COLLAB:
237-
initCollabListeners();
238-
return;
239220
default:
240221
return;
241222
}
@@ -318,14 +299,6 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
318299
});
319300
};
320301

321-
const initCollabListeners = () => {
322-
matchSocket.on(MatchEvents.MATCH_ENDED, () => {
323-
toast.error(MATCH_ENDED_MESSAGE);
324-
setIsEndSessionModalOpen(false);
325-
appNavigate(MatchPaths.HOME);
326-
});
327-
};
328-
329302
const handleMatchFound = (
330303
matchId: string,
331304
user1: MatchUser,
@@ -409,7 +382,6 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
409382
return;
410383
case MatchPaths.COLLAB:
411384
matchSocket.emit(MatchEvents.MATCH_END_REQUEST, matchUser?.id, matchId);
412-
appNavigate(MatchPaths.HOME);
413385
return;
414386
default:
415387
return;
@@ -539,8 +511,6 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
539511
partner,
540512
matchPending,
541513
loading,
542-
isEndSessionModalOpen,
543-
setIsEndSessionModalOpen,
544514
questionId,
545515
qnHistoryId,
546516
}}

0 commit comments

Comments
 (0)