Skip to content

Commit

Permalink
feat: sync judge results, closes #57
Browse files Browse the repository at this point in the history
  • Loading branch information
thecodingwizard committed Jun 21, 2022
1 parent cfed794 commit 5795ba6
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 49 deletions.
18 changes: 11 additions & 7 deletions pages/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@ import { MessagePage } from '../src/components/MessagePage';
import firebase from 'firebase/app';
import Workspace from '../src/components/Workspace/Workspace';
import {
judgeResultsAtom,
mobileActiveTabAtom,
showSidebarAtom,
inputTabAtom,
problemAtom,
tabsListAtom,
inputTabIndexAtom,
} from '../src/atoms/workspaceUI';
Expand All @@ -45,21 +43,27 @@ import { getSampleIndex } from '../src/components/JudgeInterface/Samples';
import { firebaseUserAtom } from '../src/atoms/firebaseUserAtoms';
import { useRouter } from 'next/router';
import invariant from 'tiny-invariant';
import useFirebaseRefValue from '../src/hooks/useFirebaseRefValue';
import { submitToJudge } from '../src/scripts/judge';
import useUserFileConnection from '../src/hooks/useUserFileConnection';
import useUpdateUserFilePermissions from '../src/hooks/useUpdateUserFilePermissions';
import ClassroomToolbar from '../src/components/ClassroomToolbar/ClassroomToolbar';
import { extractJavaFilename } from '../src/scripts/judge';
import useFirebaseState from '../src/hooks/useFirebaseState';

export default function EditorPage(): JSX.Element {
const [fileId, setFileId] = useAtom(fileIdAtom);
const firebaseUser = useAtomValue(firebaseUserAtom);
const layoutEditors = useUpdateAtom(layoutEditorsAtom);
const mainMonacoEditor = useAtomValue(mainMonacoEditorAtom);
const inputEditor = useAtomValue(inputMonacoEditorAtom);
const [judgeResults, setJudgeResults] = useAtom(judgeResultsAtom);
const [isRunning, setIsRunning] = useState(false);
const authenticatedFirebaseRef = useAtomValue(authenticatedFirebaseRefAtom);
const [judgeResults, setJudgeResults] = useFirebaseState<
(JudgeResult | null)[]
>(authenticatedFirebaseRef?.child('state').child('judge_results'), []);
const [isRunning, setIsRunning] = useFirebaseState(
authenticatedFirebaseRef?.child('state').child('is_running'),
false
);
const [lang, setCurrentLang] = useAtom(currentLangAtom);
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
const { settings } = useSettings();
Expand All @@ -71,7 +75,7 @@ export default function EditorPage(): JSX.Element {
const [mobileActiveTab, setMobileActiveTab] = useAtom(mobileActiveTabAtom);

const showSidebar = useAtomValue(showSidebarAtom);
const problem = useAtomValue(problemAtom);
const problem = settings.problem;
const router = useRouter();

useEffect(() => {
Expand Down Expand Up @@ -259,10 +263,10 @@ export default function EditorPage(): JSX.Element {
'. ' +
failedResult.statusDescription;
newJudgeResults[1] = failedResult;
setJudgeResults(newJudgeResults);
} else {
newJudgeResults[1] = newJudgeResults[2];
}
setJudgeResults(newJudgeResults);
} catch (e) {
console.error(e);
}
Expand Down
1 change: 0 additions & 1 deletion src/atoms/workspaceUI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ProblemData } from '../components/Workspace/Workspace';

export const mobileActiveTabAtom = atom<'code' | 'io' | 'users'>('code');
export const showSidebarAtom = atom<boolean>(false);
export const judgeResultsAtom = atom<(JudgeResult | null)[]>([]);
export const inputTabAtom = atom<string>('input');
export const problemAtom = atom<ProblemData | null | undefined>(undefined);
export const tabsListAtom = atom(get => {
Expand Down
48 changes: 13 additions & 35 deletions src/components/Workspace/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
actualUserPermissionAtom,
} from '../../atoms/workspace';
import {
judgeResultsAtom,
mobileActiveTabAtom,
showSidebarAtom,
inputTabAtom,
Expand All @@ -34,12 +33,7 @@ import JudgeResult from '../../types/judge';
import { judgePrefix } from '../JudgeInterface/JudgeInterface';
import { userSettingsAtomWithPersistence } from '../../atoms/userSettings';
import { fileIdAtom } from '../../atoms/firebaseAtoms';

function resizeResults(results: (JudgeResult | null)[], newSize: number) {
while (results.length > newSize) results.pop();
while (results.length < newSize) results.push(null);
return results;
}
import useFirebaseState from '../../hooks/useFirebaseState';

export type ProblemData = {
id: number;
Expand Down Expand Up @@ -96,8 +90,10 @@ export default function Workspace({

const permission = useAtomValue(actualUserPermissionAtom);
const readOnly = !(permission === 'OWNER' || permission === 'READ_WRITE');
const [judgeResults, setJudgeResults] = useAtom(judgeResultsAtom);

const authenticatedFirebaseRef = useAtomValue(authenticatedFirebaseRefAtom);
const [judgeResults, setJudgeResults] = useFirebaseState<
(JudgeResult | null)[]
>(authenticatedFirebaseRef?.child('state').child('judge_results'), []);
const firebaseRef = useAtomValue(authenticatedFirebaseRefAtom);
const firebaseRefs = useMemo(
() => ({
Expand All @@ -123,38 +119,20 @@ export default function Workspace({

const [statusData, setStatusData] = useState<StatusData | null>(null);

const fileId = useAtomValue(fileIdAtom);
const prevFileId = useRef('');
useEffect(() => {
const updateProblemData = (newProblem?: ProblemData | null) => {
setStatusData(null);
const newJudgeResults = judgeResults;
const newFileId = fileId?.id ?? '';
if (newFileId !== prevFileId.current) {
// changed file, clear everything
while (newJudgeResults.length > 0) newJudgeResults.pop();
newJudgeResults.push(null);
prevFileId.current = newFileId;
} else {
// don't clear input tab
while (newJudgeResults.length > 1) newJudgeResults.pop();
}
setProblem(newProblem);
if (newProblem) {
const samples = newProblem.samples;
setJudgeResults(resizeResults(newJudgeResults, 2 + samples.length));
setInputTab('judge');
} else {
setJudgeResults(newJudgeResults);
}
};
updateProblemData(settings.problem);
setStatusData(null);
setProblem(settings.problem);
if (settings.problem) {
setInputTab('judge');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [settings.problem, fileId?.id]);
}, [settings.problem]);

const inputTabIndex = useAtomValue(inputTabIndexAtom);
const { lightMode } = useAtomValue(userSettingsAtomWithPersistence);

console.log(judgeResults[inputTabIndex], judgeResults, inputTabIndex);

return (
<Split
onDragEnd={() => layoutEditors()}
Expand Down
3 changes: 0 additions & 3 deletions src/components/settings/JudgeSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// import { useAtom } from 'jotai';
import React from 'react';
import { WorkspaceSettings } from '../SettingsContext';

// import { allProblemDataAtom } from '../../atoms/workspaceUI';
// import { useAtom } from 'jotai';
import ProblemSearchInterface from './ProblemSearchInterface';

export default function JudgeSettings({
Expand Down
36 changes: 34 additions & 2 deletions src/components/settings/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import { WorkspaceSettings, useSettings } from '../SettingsContext';
import { useAtom } from 'jotai';
import { actualUserPermissionAtom } from '../../atoms/workspace';
// import { allProblemDataAtom } from '../../atoms/workspaceUI';
import { authenticatedUserRefAtom } from '../../atoms/firebaseAtoms';
import {
authenticatedFirebaseRefAtom,
authenticatedUserRefAtom,
} from '../../atoms/firebaseAtoms';
import {
displayNameAtom,
EditorMode,
Expand All @@ -29,6 +32,8 @@ import WorkspaceSettingsUI from './WorkspaceSettingsUI';
import JudgeSettings from './JudgeSettings';

import SignInSettings from './SignInSettings';
import useFirebaseState from '../../hooks/useFirebaseState';
import JudgeResult from '../../types/judge';

export interface SettingsDialogProps {
isOpen: boolean;
Expand Down Expand Up @@ -82,6 +87,11 @@ export const SettingsModal = ({
);
const [tab, setTab] = useState<typeof tabs[number]['id']>('workspace');

const authenticatedFirebaseRef = useAtomValue(authenticatedFirebaseRefAtom);
const [judgeResults, setJudgeResults] = useFirebaseState<
(JudgeResult | null)[]
>(authenticatedFirebaseRef?.child('state').child('judge_results'), []);

const [actualDisplayName, setDisplayName] = useAtom(displayNameAtom);
useEffect(() => {
if (isOpen) {
Expand Down Expand Up @@ -126,6 +136,28 @@ export const SettingsModal = ({
const { defaultPermission, ...toKeep } = settingsToSet;
settingsToSet = toKeep;
}

if (realWorkspaceSettings.problem != settingsToSet.problem) {
const newJudgeResults = judgeResults;
while (newJudgeResults.length > 1) newJudgeResults.pop();

if (settingsToSet.problem) {
function resizeResults(
results: (JudgeResult | null)[],
newSize: number
) {
while (results.length > newSize) results.pop();
while (results.length < newSize) results.push(null);
return results;
}

const samples = settingsToSet.problem.samples;
setJudgeResults(resizeResults(newJudgeResults, 2 + samples.length));
} else {
setJudgeResults(newJudgeResults);
}
}

setRealWorkspaceSettings(settingsToSet);
setUserSettings({ editorMode, tabSize, lightMode });
if (name !== actualDisplayName) {
Expand Down Expand Up @@ -271,7 +303,7 @@ export const SettingsModal = ({
className="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
onClick={() => {
onChange({
problem: undefined,
problem: null,
});
}}
>
Expand Down
1 change: 0 additions & 1 deletion src/hooks/useFirebaseRefValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export default function useFirebaseRefValue<T>(
useEffect(() => {
if (!ref) {
setIsLoading(false);
console.log('reset');
setValue(null);
return;
}
Expand Down
32 changes: 32 additions & 0 deletions src/hooks/useFirebaseState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type firebaseType from 'firebase';
import { useEffect, useMemo, useState } from 'react';

export default function useFirebaseState<T>(
ref: firebaseType.database.Reference | null,
defaultValue: T
): [T, (value: T) => void] {
const [value, setValue] = useState<T>(defaultValue);

useEffect(() => {
if (!ref) {
setValue(defaultValue);
return;
}
const callback = (snapshot: firebaseType.database.DataSnapshot) => {
const val = snapshot.val();
setValue(val ?? defaultValue);
};
ref.on('value', callback);
return () => ref.off('value', callback);
}, [ref?.key]);

const update = useMemo(() => {
if (!ref) return () => {};

return (value: T) => {
ref.set(value);
};
}, [ref]);

return useMemo(() => [value, update], [value, update]);
}

0 comments on commit 5795ba6

Please sign in to comment.