diff --git a/pages/[id].tsx b/pages/[id].tsx index dac4216..7a608cc 100644 --- a/pages/[id].tsx +++ b/pages/[id].tsx @@ -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'; @@ -45,12 +43,12 @@ 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); @@ -58,8 +56,14 @@ export default function EditorPage(): JSX.Element { 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(); @@ -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(() => { @@ -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); } diff --git a/src/atoms/workspaceUI.ts b/src/atoms/workspaceUI.ts index 045c6d8..d09ca65 100644 --- a/src/atoms/workspaceUI.ts +++ b/src/atoms/workspaceUI.ts @@ -4,7 +4,6 @@ import { ProblemData } from '../components/Workspace/Workspace'; export const mobileActiveTabAtom = atom<'code' | 'io' | 'users'>('code'); export const showSidebarAtom = atom(false); -export const judgeResultsAtom = atom<(JudgeResult | null)[]>([]); export const inputTabAtom = atom('input'); export const problemAtom = atom(undefined); export const tabsListAtom = atom(get => { diff --git a/src/components/Workspace/Workspace.tsx b/src/components/Workspace/Workspace.tsx index e47d885..b91f7e4 100644 --- a/src/components/Workspace/Workspace.tsx +++ b/src/components/Workspace/Workspace.tsx @@ -13,7 +13,6 @@ import { actualUserPermissionAtom, } from '../../atoms/workspace'; import { - judgeResultsAtom, mobileActiveTabAtom, showSidebarAtom, inputTabAtom, @@ -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; @@ -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( () => ({ @@ -123,38 +119,20 @@ export default function Workspace({ const [statusData, setStatusData] = useState(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 ( layoutEditors()} diff --git a/src/components/settings/JudgeSettings.tsx b/src/components/settings/JudgeSettings.tsx index 4225a23..5598823 100644 --- a/src/components/settings/JudgeSettings.tsx +++ b/src/components/settings/JudgeSettings.tsx @@ -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({ diff --git a/src/components/settings/SettingsModal.tsx b/src/components/settings/SettingsModal.tsx index da657f2..bedc0bc 100644 --- a/src/components/settings/SettingsModal.tsx +++ b/src/components/settings/SettingsModal.tsx @@ -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, @@ -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; @@ -82,6 +87,11 @@ export const SettingsModal = ({ ); const [tab, setTab] = useState('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) { @@ -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) { @@ -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, }); }} > diff --git a/src/hooks/useFirebaseRefValue.ts b/src/hooks/useFirebaseRefValue.ts index 95592ab..92bfe9c 100644 --- a/src/hooks/useFirebaseRefValue.ts +++ b/src/hooks/useFirebaseRefValue.ts @@ -11,7 +11,6 @@ export default function useFirebaseRefValue( useEffect(() => { if (!ref) { setIsLoading(false); - console.log('reset'); setValue(null); return; } diff --git a/src/hooks/useFirebaseState.ts b/src/hooks/useFirebaseState.ts new file mode 100644 index 0000000..42f4079 --- /dev/null +++ b/src/hooks/useFirebaseState.ts @@ -0,0 +1,32 @@ +import type firebaseType from 'firebase'; +import { useEffect, useMemo, useState } from 'react'; + +export default function useFirebaseState( + ref: firebaseType.database.Reference | null, + defaultValue: T +): [T, (value: T) => void] { + const [value, setValue] = useState(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]); +}