Skip to content

Commit 22a497a

Browse files
committed
Add UI for Comms, switched off StrictMode
Switched off StrictMode to reason with the websockets better. Use strict mode at your own risk!
1 parent 0c90257 commit 22a497a

File tree

7 files changed

+457
-36
lines changed

7 files changed

+457
-36
lines changed

peerprep/app/questions/[question]/[roomID]/question.tsx

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22
import React from "react";
33
import { Difficulty, Question } from "@/api/structs";
44
import Chip from "@/components/shared/Chip";
5-
import PeerprepButton from "@/components/shared/PeerprepButton";
65
import styles from "@/style/question.module.css";
7-
import { useRouter } from "next/navigation";
8-
import { deleteQuestion } from "@/app/api/internal/questions/helper";
96
import CollabEditor from "@/components/questionpage/CollabEditor";
107
import DOMPurify from "isomorphic-dompurify";
118

@@ -31,28 +28,6 @@ function DifficultyChip({ diff }: DifficultyChipProps) {
3128
}
3229

3330
function QuestionBlock({ question, roomID, authToken, matchHash }: Props) {
34-
const router = useRouter();
35-
36-
const handleDelete = async () => {
37-
if (
38-
confirm(
39-
`Are you sure you want to delete ${question.title}? (ID: ${question.id}) `
40-
)
41-
) {
42-
const status = await deleteQuestion(question.id);
43-
if (status.error) {
44-
alert(
45-
`Failed to delete question. Code ${status.status}: ${status.error}`
46-
);
47-
return;
48-
}
49-
console.log(`Successfully deleted the question.`);
50-
router.push("/questions");
51-
} else {
52-
console.log("Deletion cancelled.");
53-
}
54-
};
55-
5631
return (
5732
<>
5833
<div className={styles.qn_container}>
@@ -63,12 +38,6 @@ function QuestionBlock({ question, roomID, authToken, matchHash }: Props) {
6338
</h1>
6439
<DifficultyChip diff={question.difficulty} />
6540
</div>
66-
<PeerprepButton
67-
className={` ${styles.button}`}
68-
onClick={handleDelete}
69-
>
70-
Delete
71-
</PeerprepButton>
7241
</div>
7342
<div className={styles.label_wrapper}>
7443
<p>Topics: </p>

peerprep/components/questionpage/CollabEditor.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import PeerprepDropdown from "@/components/shared/PeerprepDropdown";
1414

1515
import { Question } from "@/api/structs";
1616
import PeerprepButton from "../shared/PeerprepButton";
17+
import CommsPanel from "./CommsPanel";
1718

1819
const languages = [
1920
"javascript",
@@ -51,7 +52,12 @@ interface Props {
5152
matchHash?: String;
5253
}
5354

54-
export default function CollabEditor({ question, roomID, authToken, matchHash }: Props) {
55+
export default function CollabEditor({
56+
question,
57+
roomID,
58+
authToken,
59+
matchHash,
60+
}: Props) {
5561
const [theme, setTheme] = useState("terminal");
5662
const [fontSize, setFontSize] = useState(18);
5763
const [language, setLanguage] = useState("python");
@@ -76,7 +82,7 @@ export default function CollabEditor({ question, roomID, authToken, matchHash }:
7682
useEffect(() => {
7783
if (!roomID) return;
7884

79-
console.log("Yep");
85+
console.log("Testing http");
8086

8187
const newSocket = new WebSocket(`/api/proxy?roomID=${roomID}`);
8288

@@ -87,7 +93,7 @@ export default function CollabEditor({ question, roomID, authToken, matchHash }:
8793
const authMessage = {
8894
type: "auth",
8995
token: authToken,
90-
matchHash: matchHash // omitted if undefined
96+
matchHash: matchHash, // omitted if undefined
9197
};
9298
newSocket.send(JSON.stringify(authMessage));
9399
};
@@ -114,8 +120,9 @@ export default function CollabEditor({ question, roomID, authToken, matchHash }:
114120
}
115121
};
116122

117-
newSocket.onerror = () => {
123+
newSocket.onerror = (e) => {
118124
console.log("server down");
125+
console.log(e);
119126
};
120127

121128
newSocket.onclose = () => {
@@ -143,6 +150,7 @@ export default function CollabEditor({ question, roomID, authToken, matchHash }:
143150

144151
return (
145152
<>
153+
<CommsPanel className="flex flex-row justify-around" roomId={roomID}/>
146154
<div className="flex space-x-4 items-center p-4 m-4">
147155
<div className="flex flex-col">
148156
<label className="font-semibold mb-1">Font Size</label>
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import React, { useEffect, useRef, useState } from "react";
2+
import Peer, { Instance } from "simple-peer";
3+
import io from "socket.io-client";
4+
5+
interface Props {
6+
className?: string;
7+
roomId?: String;
8+
}
9+
10+
const socket = io("http://localhost:4001");
11+
12+
function CommsPanel({ className, roomId }: Props) {
13+
const [stream, setStream] = useState<MediaStream>();
14+
15+
const myVideo = useRef<HTMLVideoElement>(null);
16+
const userVideo = useRef<HTMLVideoElement>(null);
17+
const connectionRef = useRef<Instance>();
18+
19+
useEffect(() => {
20+
socket.removeAllListeners();
21+
socket.open();
22+
return () => {
23+
console.log("socket cleanup called");
24+
if (socket) {
25+
console.log("destroying socket");
26+
socket.close();
27+
}
28+
if (connectionRef.current) {
29+
connectionRef.current.destroy();
30+
}
31+
};
32+
}, []);
33+
34+
useEffect(() => {
35+
// capture the stream within the cleanup function itself.
36+
let videoElement: MediaStream | undefined;
37+
38+
navigator.mediaDevices
39+
.getUserMedia({ video: true, audio: true })
40+
.then((newStream) => {
41+
console.log("new stream's status is " + newStream.active);
42+
newStream.getTracks().forEach((track: MediaStreamTrack) => {
43+
console.log("media track status (ready/enabled): " + track.readyState + "/" + track.enabled);
44+
})
45+
if (myVideo.current) {
46+
console.log("can set myVideo.current")
47+
myVideo.current.srcObject = newStream;
48+
}
49+
setStream(newStream);
50+
videoElement = newStream;
51+
}).catch((err) => console.log("failed to get stream"));
52+
53+
return () => {
54+
console.log("cleaning up media");
55+
if (videoElement) {
56+
console.log("destroying stream");
57+
videoElement.getTracks().forEach((track) => track.stop());
58+
}
59+
}
60+
}, [])
61+
62+
useEffect(() => {
63+
if (!roomId || !stream || !socket.connected) {
64+
return;
65+
}
66+
console.log("in hook");
67+
68+
// clear all listeners if we are reinitializing this.
69+
socket.removeAllListeners();
70+
console.log("removed all listeners");
71+
72+
// when we receive the first peer connection, we immediately send out
73+
// a peer connection request.
74+
attachSocketInitiator(stream, roomId, userVideo, connectionRef);
75+
76+
// as the receiver, I will propagate my data outwards now.
77+
attachSocketReceiver(stream, roomId, userVideo, connectionRef);
78+
79+
socket.on("endCall", () => {
80+
if (userVideo.current) {
81+
(userVideo.current.srcObject as MediaStream)
82+
.getTracks().forEach((tracks: MediaStreamTrack) => {
83+
tracks.stop()
84+
});
85+
userVideo.current.srcObject = null;
86+
}
87+
});
88+
89+
socket.emit("joinRoom", {
90+
target: roomId,
91+
});
92+
console.log("applied all hooks");
93+
}, [stream, socket.connected]);
94+
95+
return (
96+
<div className={className}>
97+
<div className="video">
98+
<video
99+
playsInline
100+
muted
101+
ref={myVideo}
102+
autoPlay
103+
style={{ width: "200px" }}
104+
/>
105+
</div>
106+
<div className="video">
107+
<video
108+
playsInline
109+
ref={userVideo}
110+
autoPlay
111+
style={{ width: "200px" }}
112+
/>
113+
</div>
114+
</div>
115+
);
116+
}
117+
118+
function attachSocketReceiver(
119+
stream: MediaStream, roomId: String,
120+
userVideo: React.RefObject<HTMLVideoElement>,
121+
connectionRef: React.MutableRefObject<Peer.Instance|undefined>
122+
) {
123+
socket.on("startCall", (data) => {
124+
console.log("received start call signal");
125+
const peerReceive = new Peer({
126+
initiator: false,
127+
trickle: false,
128+
stream: stream,
129+
});
130+
131+
peerReceive.on("signal", (data) => {
132+
console.log("sending handshake");
133+
socket.emit("handshakeCall", { signal: data, target: roomId });
134+
});
135+
136+
peerReceive.on("stream", (stream) => {
137+
console.log("setting stream of first user");
138+
if (userVideo.current) {
139+
console.log("user video exists");
140+
userVideo.current.srcObject = stream;
141+
}
142+
});
143+
144+
connectionRef.current = peerReceive;
145+
console.log("signalling receiver");
146+
peerReceive.signal(data.signal);
147+
});
148+
}
149+
150+
function attachSocketInitiator(
151+
stream: MediaStream, roomId: String,
152+
userVideo: React.RefObject<HTMLVideoElement>,
153+
connectionRef: React.MutableRefObject<Peer.Instance|undefined>
154+
) {
155+
socket.on("peerConnected", () => {
156+
console.log("peer connected, starting call");
157+
const peerInit = new Peer({
158+
initiator: true,
159+
trickle: false,
160+
stream: stream,
161+
});
162+
163+
peerInit.on("signal", (data) => {
164+
console.log("signal to start call received");
165+
socket.emit("startCall", { signalData: data, target: roomId });
166+
});
167+
168+
peerInit.on("stream", (stream) => {
169+
if (userVideo.current) {
170+
console.log("setting stream for handshake");
171+
userVideo.current.srcObject = stream;
172+
}
173+
});
174+
175+
connectionRef.current = peerInit;
176+
177+
socket.on("handshakeCall", (data) => {
178+
console.log("received handshake");
179+
peerInit.signal(data.signal)
180+
});
181+
});
182+
}
183+
184+
export default CommsPanel;

peerprep/next.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
const nextConfig = {}
33

44
module.exports = {
5+
reactStrictMode: false,
56
async rewrites() {
67
return [
78
{

0 commit comments

Comments
 (0)