Skip to content

Commit

Permalink
Merge pull request #71 from CS3219-AY2425S1/titus/fix-local-storage-bug
Browse files Browse the repository at this point in the history
fix(frontend): 🐛 mount component before accessing local storage
  • Loading branch information
bensohh authored Nov 12, 2024
2 parents 7d931ee + 3fd5752 commit 31a6bd1
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 34 deletions.
59 changes: 55 additions & 4 deletions apps/frontend/src/app/collaboration/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ import {
ExecuteVisibleAndHiddenTestsAndSubmit,
ExecutionResults,
GetVisibleTests,
isTestResult,
SubmissionHiddenTestResultsAndStatus,
SubmissionResults,
Test,
TestData,
TestResult,
} from "@/app/services/execute";
import { QuestionDetailFull } from "@/components/question/QuestionDetailFull/QuestionDetailFull";
import VideoPanel from "@/components/VideoPanel/VideoPanel";
Expand Down Expand Up @@ -75,15 +78,17 @@ export default function CollaborationPage(props: CollaborationProps) {
);
const [currentUser, setCurrentUser] = useState<string | undefined>(undefined);
const [matchedUser, setMatchedUser] = useState<string>("Loading...");
const [sessionDuration, setSessionDuration] = useState<number>(() => {
const storedTime = localStorage.getItem("session-duration");
return storedTime ? parseInt(storedTime) : 0;
}); // State for count-up timer (TODO: currently using localstorage to store time, change to db stored time in the future)
const [sessionDuration, setSessionDuration] = useState<number>(0); // State for count-up timer (TODO: currently using localstorage to store time, change to db stored time in the future)
const stopwatchRef = useRef<NodeJS.Timeout | null>(null);
const [matchedTopics, setMatchedTopics] = useState<string[] | undefined>(
undefined
);

useEffect(() => {
const storedTime = localStorage.getItem("session-duration");
setSessionDuration(storedTime ? parseInt(storedTime) : 0);
}, []);

// Chat states
const [messageToSend, setMessageToSend] = useState<string | undefined>(
undefined
Expand Down Expand Up @@ -314,6 +319,52 @@ export default function CollaborationPage(props: CollaborationProps) {
}
}, [isSessionEndModalOpen, countDown]);

// Tabs component items for visibleTestCases
var items: TabsProps["items"] = visibleTestCases.map((item, index) => {
return {
key: index.toString(),
label: (
<span
style={{
color: !isTestResult(item) ? "" : item.passed ? "green" : "red",
}}
>
Case {index + 1}
</span>
),
children: (
<div>
<Input.TextArea
disabled
placeholder={`Stdin: ${item.input}\nStdout: ${item.expected}`}
rows={4}
/>
{isTestResult(item) && (
<div className="test-result-container">
<InfoCircleFilled className="hidden-test-icon" />
<Typography.Text
strong
style={{ color: item.passed ? "green" : "red" }}
>
{item.passed ? "Passed" : "Failed"}
</Typography.Text>
<br />
<Typography.Text strong>Actual Output:</Typography.Text>{" "}
{item.actual}
<br />
{item.error && (
<>
<Typography.Text strong>Error:</Typography.Text>
<div className="error-message">{item.error}</div>
</>
)}
</div>
)}
</div>
),
};
});

// Handles the cleaning of localstorage variables, stopping the timer & signalling collab user on webrtc
// type: "initiator" | "peer"
const handleCloseCollaboration = (type: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import * as Y from "yjs";
import { yCollab } from "y-codemirror.next";
import { WebrtcProvider } from "y-webrtc";
import { EditorView, basicSetup } from "codemirror";
import { keymap } from "@codemirror/view"
import { indentWithTab } from "@codemirror/commands"
import { keymap } from "@codemirror/view";
import { indentWithTab } from "@codemirror/commands";
import { EditorState, Compartment } from "@codemirror/state";
import { javascript, javascriptLanguage } from "@codemirror/lang-javascript";
import { python, pythonLanguage } from "@codemirror/lang-python";
Expand Down Expand Up @@ -68,15 +68,15 @@ interface Awareness {
executionResultsState: {
executionResults: ExecutionResults;
id: number;
}
};
executingState: {
executing: boolean;
id: number;
}
};
submittingState: {
submitting: boolean;
id: number;
}
};
}

export const usercolors = [
Expand Down Expand Up @@ -111,8 +111,7 @@ const CollaborativeEditor = forwardRef(
props.onCodeChange(update.state.doc.toString());
}
});



// Referenced: https://codemirror.net/examples/config/#dynamic-configuration
// const autoLanguage = EditorState.transactionExtender.of((tr) => {
// if (!tr.docChanged) return null;
Expand Down Expand Up @@ -196,10 +195,10 @@ const CollaborativeEditor = forwardRef(
});
};

let latestExecutionId: number = (new Date(0)).getTime();
let latestSubmissionId: number = (new Date(0)).getTime();
let latestExecutingId: number = (new Date(0)).getTime();
let latestSubmittingId: number = (new Date(0)).getTime();
let latestExecutionId: number = new Date(0).getTime();
let latestSubmissionId: number = new Date(0).getTime();
let latestExecutingId: number = new Date(0).getTime();
let latestSubmittingId: number = new Date(0).getTime();

useImperativeHandle(ref, () => ({
endSession: () => {
Expand Down Expand Up @@ -311,12 +310,14 @@ const CollaborativeEditor = forwardRef(
.get(clientID) as Awareness;

if (
state &&
state &&
state.submissionResultsState &&
state.submissionResultsState.id !== latestSubmissionId
) {
latestSubmissionId = state.submissionResultsState.id;
props.updateSubmissionResults(state.submissionResultsState.submissionResults);
props.updateSubmissionResults(
state.submissionResultsState.submissionResults
);
messageApi.open({
type: "success",
content: `${
Expand All @@ -326,12 +327,14 @@ const CollaborativeEditor = forwardRef(
}

if (
state &&
state.executionResultsState &&
state &&
state.executionResultsState &&
state.executionResultsState.id !== latestExecutionId
) {
latestExecutionId = state.executionResultsState.id;
props.updateExecutionResults(state.executionResultsState.executionResults);
props.updateExecutionResults(
state.executionResultsState.executionResults
);
messageApi.open({
type: "success",
content: `${
Expand All @@ -341,8 +344,8 @@ const CollaborativeEditor = forwardRef(
}

if (
state &&
state.executingState &&
state &&
state.executingState &&
state.executingState.id !== latestExecutingId
) {
latestExecutingId = state.executingState.id;
Expand All @@ -358,18 +361,16 @@ const CollaborativeEditor = forwardRef(
}

if (
state &&
state.submittingState &&
state &&
state.submittingState &&
state.submittingState.id !== latestSubmittingId
) {
latestSubmittingId = state.submittingState.id;
props.updateSubmitting(state.submittingState.submitting);
if (state.submittingState.submitting) {
messageApi.open({
type: "info",
content: `${
props.matchedUser ?? "Peer"
} is saving code...`,
content: `${props.matchedUser ?? "Peer"} is saving code...`,
});
}
}
Expand Down
20 changes: 13 additions & 7 deletions apps/frontend/src/components/VideoPanel/VideoPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@ import {
} from "@ant-design/icons";

const VideoPanel = () => {
const matchId = localStorage.getItem("collabId")?.toString() ?? "";
const currentUsername = localStorage.getItem("user")?.toString();
const matchedUsername = localStorage.getItem("matchedUser")?.toString();
const currentId = currentUsername + "-" + matchId ?? "";
const partnerId = matchedUsername + "-" + matchId ?? "";
const [currentId, setCurrentId] = useState<string | undefined>();
const [partnerId, setPartnerId] = useState<string | undefined>();

const remoteVideoRef = useRef<HTMLVideoElement>(null);
const currentUserVideoRef = useRef<HTMLVideoElement>(null);
Expand All @@ -30,14 +27,23 @@ const VideoPanel = () => {
const [muteOn, setMuteOn] = useState<boolean>(false);
const [isCalling, setIsCalling] = useState<boolean>(false);

useEffect(() => {
const matchId = localStorage.getItem("collabId")?.toString() ?? "";
const currentUsername = localStorage.getItem("user")?.toString();
const matchedUsername = localStorage.getItem("matchedUser")?.toString();

setCurrentId(currentUsername + "-" + (matchId ?? ""));
setPartnerId(matchedUsername + "-" + (matchId ?? ""));
}, []);

const handleCall = () => {
navigator.mediaDevices
.getUserMedia({
video: true,
audio: true,
})
.then((stream) => {
if (peerInstance) {
if (peerInstance && partnerId) {
const call = peerInstance?.call(partnerId, stream);
setCallInstance(call);
setIsCalling(true); // Set isCalling as true since it is the initiator
Expand Down Expand Up @@ -120,7 +126,7 @@ const VideoPanel = () => {
}
};
}
}, []);
}, [currentId]);

// When remote peer initiates end call, we set isCalling to false
useEffect(() => {
Expand Down

0 comments on commit 31a6bd1

Please sign in to comment.