diff --git a/src/webview/frontend/App.tsx b/src/webview/frontend/App.tsx index 3b5dbb9..442ae75 100644 --- a/src/webview/frontend/App.tsx +++ b/src/webview/frontend/App.tsx @@ -1,14 +1,14 @@ import React, { useState, useEffect } from 'react'; import { createRoot } from 'react-dom/client'; import { - Problem, - WebviewToVSEvent, - TestCase, - Case, - VSToWebViewMessage, - ResultCommand, - RunningCommand, - WebViewpersistenceState, + Problem, + WebviewToVSEvent, + TestCase, + Case, + VSToWebViewMessage, + ResultCommand, + RunningCommand, + WebViewpersistenceState, } from '../../types'; import CaseView from './CaseView'; import Page from './Page'; @@ -18,30 +18,30 @@ let notificationTimeout: NodeJS.Timeout | undefined = undefined; const originalConsole = { ...window.console }; function customLogger( - originalMethod: (...args: any[]) => void, - ...args: any[] + originalMethod: (...args: any[]) => void, + ...args: any[] ) { - originalMethod(...args); + originalMethod(...args); - storedLogs += new Date().toISOString() + ' '; - storedLogs += - args - .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : arg)) - .join(' ') + '\n'; + storedLogs += new Date().toISOString() + ' '; + storedLogs += + args + .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : arg)) + .join(' ') + '\n'; } declare const vscodeApi: { - postMessage: (message: WebviewToVSEvent) => void; - getState: () => WebViewpersistenceState | undefined; - setState: (state: WebViewpersistenceState) => void; + postMessage: (message: WebviewToVSEvent) => void; + getState: () => WebViewpersistenceState | undefined; + setState: (state: WebViewpersistenceState) => void; }; interface CustomWindow extends Window { - generatedJsonUri: string; - remoteMessage: string | null; - remoteServerAddress: string; - showLiveUserCount: boolean; - console: Console; + generatedJsonUri: string; + remoteMessage: string | null; + remoteServerAddress: string; + showLiveUserCount: boolean; + console: Console; } declare const window: CustomWindow; @@ -55,698 +55,698 @@ window.console.debug = customLogger.bind(window.console, originalConsole.debug); const payPalUrl = 'https://rb.gy/5iiorz'; function getLiveUserCount(): Promise { - console.log('Fetching live user count'); - return fetch(window.remoteServerAddress) - .then((res) => res.text()) - .then((text) => { - const userCount = Number(text); - if (isNaN(userCount)) { - console.error('Invalid live user count', text); - return 0; - } else { - return userCount; - } - }) - .catch((err) => { - console.error('Failed to fetch live users', err); - return 0; - }); + console.log('Fetching live user count'); + return fetch(window.remoteServerAddress) + .then((res) => res.text()) + .then((text) => { + const userCount = Number(text); + if (isNaN(userCount)) { + console.error('Invalid live user count', text); + return 0; + } else { + return userCount; + } + }) + .catch((err) => { + console.error('Failed to fetch live users', err); + return 0; + }); } function Judge(props: { - problem: Problem; - updateProblem: (problem: Problem) => void; - cases: Case[]; - updateCases: (cases: Case[]) => void; + problem: Problem; + updateProblem: (problem: Problem) => void; + cases: Case[]; + updateCases: (cases: Case[]) => void; }) { - const problem = props.problem; - const cases = props.cases; - const updateProblem = props.updateProblem; - const updateCases = props.updateCases; - - const [focusLast, setFocusLast] = useState(false); - const [forceRunning, setForceRunning] = useState(false); - const [compiling, setCompiling] = useState(false); - const [notification, setNotification] = useState(null); - const [waitingForSubmit, setWaitingForSubmit] = useState(false); - const [onlineJudgeEnv, setOnlineJudgeEnv] = useState(false); - const [infoPageVisible, setInfoPageVisible] = useState(false); - const [generatedJson, setGeneratedJson] = useState(null); - const [liveUserCount, setLiveUserCount] = useState(0); - const [extLogs, setExtLogs] = useState(''); - - useEffect(() => { - const updateLiveUserCount = (): void => { - if (window.showLiveUserCount) { - getLiveUserCount().then((count) => setLiveUserCount(count)); - } - }; - updateLiveUserCount(); - const interval = setInterval(updateLiveUserCount, 30000); - return () => clearInterval(interval); - }, []); - - useEffect(() => { - fetch(window.generatedJsonUri) - .then((res) => res.json()) - .then((data) => setGeneratedJson(data)) - .catch((err) => - console.error('Failed to fetch generated JSON', err), - ); - }, []); - - const [webviewState, setWebviewState] = useState( - () => { - const vscodeState = vscodeApi.getState(); - const ret = { - dialogCloseDate: vscodeState?.dialogCloseDate || Date.now(), - }; - vscodeApi.setState(ret); - console.log('Restored to state:', ret); - return ret; - }, - ); - - console.log(webviewState); - - // Update problem if cases change. The only place where `updateProblem` is - // allowed to ensure sync. - useEffect(() => { - const testCases: TestCase[] = cases.map((c) => c.testcase); - updateProblem({ - ...problem, - tests: testCases, - }); - }, [cases]); - - const closeDonateBox = () => { - const newState = { - ...webviewState, - dialogCloseDate: Date.now(), - }; - setWebviewState(newState); - vscodeApi.setState(newState); - }; - - const sendMessageToVSCode = (message: WebviewToVSEvent) => { - vscodeApi.postMessage(message); - }; - - useEffect(() => { - const fn = (event: any) => { - const data: VSToWebViewMessage = event.data; - switch (data.command) { - case 'new-problem': { - setOnlineJudgeEnv(false); - break; - } - - case 'remote-message': { - window.remoteMessage = data.message; - break; - } - - case 'running': { - handleRunning(data); - break; - } - case 'run-all': { - runAll(); - break; - } - case 'compiling-start': { - setCompiling(true); - break; - } - case 'compiling-stop': { - setCompiling(false); - break; - } - case 'submit-finished': { - setWaitingForSubmit(false); - break; - } - case 'waiting-for-submit': { - setWaitingForSubmit(true); - break; - } - case 'ext-logs': { - setExtLogs(data.logs); - break; - } - default: { - console.log('Invalid event', event.data); - } - } - }; - window.addEventListener('message', fn); - return () => { - window.removeEventListener('message', fn); - }; - }, []); - - const handleRunning = (data: RunningCommand) => { - setForceRunning(data.id); + const problem = props.problem; + const cases = props.cases; + const updateProblem = props.updateProblem; + const updateCases = props.updateCases; + + const [focusLast, setFocusLast] = useState(false); + const [forceRunning, setForceRunning] = useState(false); + const [compiling, setCompiling] = useState(false); + const [notification, setNotification] = useState(null); + const [waitingForSubmit, setWaitingForSubmit] = useState(false); + const [onlineJudgeEnv, setOnlineJudgeEnv] = useState(false); + const [infoPageVisible, setInfoPageVisible] = useState(false); + const [generatedJson, setGeneratedJson] = useState(null); + const [liveUserCount, setLiveUserCount] = useState(0); + const [extLogs, setExtLogs] = useState(''); + + useEffect(() => { + const updateLiveUserCount = (): void => { + if (window.showLiveUserCount) { + getLiveUserCount().then((count) => setLiveUserCount(count)); + } }; + updateLiveUserCount(); + const interval = setInterval(updateLiveUserCount, 30000); + return () => clearInterval(interval); + }, []); + + useEffect(() => { + fetch(window.generatedJsonUri) + .then((res) => res.json()) + .then((data) => setGeneratedJson(data)) + .catch((err) => + console.error('Failed to fetch generated JSON', err), + ); + }, []); + + const [webviewState, setWebviewState] = useState( + () => { + const vscodeState = vscodeApi.getState(); + const ret = { + dialogCloseDate: vscodeState?.dialogCloseDate || Date.now(), + }; + vscodeApi.setState(ret); + console.log('Restored to state:', ret); + return ret; + }, + ); + + console.log(webviewState); + + // Update problem if cases change. The only place where `updateProblem` is + // allowed to ensure sync. + useEffect(() => { + const testCases: TestCase[] = cases.map((c) => c.testcase); + updateProblem({ + ...problem, + tests: testCases, + }); + }, [cases]); - const refreshOnlineJudge = () => { - sendMessageToVSCode({ - command: 'online-judge-env', - value: onlineJudgeEnv, - }); + const closeDonateBox = () => { + const newState = { + ...webviewState, + dialogCloseDate: Date.now(), }; - - const rerun = (id: number, input: string, output: string) => { - refreshOnlineJudge(); - const idx = problem.tests.findIndex((testCase) => testCase.id === id); - - if (idx === -1) { - console.log('No id in problem tests', problem, id); - return; + setWebviewState(newState); + vscodeApi.setState(newState); + }; + + const sendMessageToVSCode = (message: WebviewToVSEvent) => { + vscodeApi.postMessage(message); + }; + + useEffect(() => { + const fn = (event: any) => { + const data: VSToWebViewMessage = event.data; + switch (data.command) { + case 'new-problem': { + setOnlineJudgeEnv(false); + break; } - problem.tests[idx].input = input; - problem.tests[idx].output = output; - - sendMessageToVSCode({ - command: 'run-single-and-save', - problem, - id, - }); - }; - - // Remove a case. - const remove = (id: number) => { - const newCases = cases.filter((value) => value.id !== id); - updateCases(newCases); - }; - - // Create a new Case - const newCase = () => { - const id = Date.now(); - const testCase: TestCase = { - id, - input: '', - output: '', - }; - updateCases([ - ...cases, - { - id, - result: null, - testcase: testCase, - }, - ]); - setFocusLast(true); - }; - - // Stop running executions. - const stop = () => { - notify('Stopped any running processes'); - sendMessageToVSCode({ - command: 'kill-running', - problem, - }); - }; - - // Deletes the .prob file and closes webview - const deleteTcs = () => { - sendMessageToVSCode({ - command: 'delete-tcs', - problem, - }); - }; + case 'remote-message': { + window.remoteMessage = data.message; + break; + } - const runAll = () => { - refreshOnlineJudge(); - sendMessageToVSCode({ - command: 'run-all-and-save', - problem, - }); + case 'running': { + handleRunning(data); + break; + } + case 'run-all': { + runAll(); + break; + } + case 'compiling-start': { + setCompiling(true); + break; + } + case 'compiling-stop': { + setCompiling(false); + break; + } + case 'submit-finished': { + setWaitingForSubmit(false); + break; + } + case 'waiting-for-submit': { + setWaitingForSubmit(true); + break; + } + case 'ext-logs': { + setExtLogs(data.logs); + break; + } + default: { + console.log('Invalid event', event.data); + } + } }; - - const submitKattis = () => { - sendMessageToVSCode({ - command: 'submitKattis', - problem, - }); - - setWaitingForSubmit(true); + window.addEventListener('message', fn); + return () => { + window.removeEventListener('message', fn); }; + }, []); - const submitCf = () => { - sendMessageToVSCode({ - command: 'submitCf', - problem, - }); + const handleRunning = (data: RunningCommand) => { + setForceRunning(data.id); + }; - setWaitingForSubmit(true); - }; + const refreshOnlineJudge = () => { + sendMessageToVSCode({ + command: 'online-judge-env', + value: onlineJudgeEnv, + }); + }; - const debounceFocusLast = () => { - setTimeout(() => { - setFocusLast(false); - }, 100); - }; + const rerun = (id: number, input: string, output: string) => { + refreshOnlineJudge(); + const idx = problem.tests.findIndex((testCase) => testCase.id === id); - const debounceForceRunning = () => { - setTimeout(() => { - setForceRunning(false); - }, 100); - }; + if (idx === -1) { + console.log('No id in problem tests', problem, id); + return; + } - const getRunningProp = (value: Case) => { - if (forceRunning === value.id) { - debounceForceRunning(); - return forceRunning === value.id; - } - return false; - }; + problem.tests[idx].input = input; + problem.tests[idx].output = output; - const toggleOnlineJudgeEnv = () => { - const newEnv = !onlineJudgeEnv; - setOnlineJudgeEnv(newEnv); - sendMessageToVSCode({ - command: 'online-judge-env', - value: newEnv, - }); - }; - - const updateCase = (id: number, input: string, output: string) => { - const newCases: Case[] = cases.map((testCase) => { - if (testCase.id === id) { - return { - id, - result: testCase.result, - testcase: { - id, - input, - output, - }, - }; - } else { - return testCase; - } - }); - updateCases(newCases); + sendMessageToVSCode({ + command: 'run-single-and-save', + problem, + id, + }); + }; + + // Remove a case. + const remove = (id: number) => { + const newCases = cases.filter((value) => value.id !== id); + updateCases(newCases); + }; + + // Create a new Case + const newCase = () => { + const id = Date.now(); + const testCase: TestCase = { + id, + input: '', + output: '', }; + updateCases([ + ...cases, + { + id, + result: null, + testcase: testCase, + }, + ]); + setFocusLast(true); + }; + + // Stop running executions. + const stop = () => { + notify('Stopped any running processes'); + sendMessageToVSCode({ + command: 'kill-running', + problem, + }); + }; - const notify = (text: string) => { - clearTimeout(notificationTimeout!); - setNotification(text); - notificationTimeout = setTimeout(() => { - setNotification(null); - notificationTimeout = undefined; - }, 1000); - }; + // Deletes the .prob file and closes webview + const deleteTcs = () => { + sendMessageToVSCode({ + command: 'delete-tcs', + problem, + }); + }; - const views: JSX.Element[] = []; - cases.forEach((value, index) => { - if (focusLast && index === cases.length - 1) { - views.push( - , - ); - debounceFocusLast(); - } else { - views.push( - , - ); - } + const runAll = () => { + refreshOnlineJudge(); + sendMessageToVSCode({ + command: 'run-all-and-save', + problem, }); + }; - const renderSubmitButton = () => { - if (!problem.url.startsWith('http')) { - return null; - } + const submitKattis = () => { + sendMessageToVSCode({ + command: 'submitKattis', + problem, + }); - let url: URL; - try { - url = new URL(problem.url); - } catch (err) { - console.error(err, problem); - return null; - } - if ( - url.hostname !== 'codeforces.com' && - url.hostname !== 'open.kattis.com' - ) { - return null; - } + setWaitingForSubmit(true); + }; - if (url.hostname == 'codeforces.com') { - return ( - - ); - } else if (url.hostname == 'open.kattis.com') { - return ( -
- - {waitingForSubmit && ( - <> - Submitting... -
- - To submit to Kattis, you need to have the{' '} - - submission client{' '} - - and the{' '} - - configuration file{' '} - - downloaded in a folder called .kattis in your - home directory. -
- Submission result will open in your browser. -
-
-
- - )} -
- ); - } - }; + const submitCf = () => { + sendMessageToVSCode({ + command: 'submitCf', + problem, + }); - const getHref = () => { - if (problem.local === undefined || problem.local === false) { - return problem.url; - } else { - return undefined; - } - }; + setWaitingForSubmit(true); + }; + + const debounceFocusLast = () => { + setTimeout(() => { + setFocusLast(false); + }, 100); + }; + + const debounceForceRunning = () => { + setTimeout(() => { + setForceRunning(false); + }, 100); + }; + + const getRunningProp = (value: Case) => { + if (forceRunning === value.id) { + debounceForceRunning(); + return forceRunning === value.id; + } + return false; + }; + + const toggleOnlineJudgeEnv = () => { + const newEnv = !onlineJudgeEnv; + setOnlineJudgeEnv(newEnv); + sendMessageToVSCode({ + command: 'online-judge-env', + value: newEnv, + }); + }; + + const updateCase = (id: number, input: string, output: string) => { + const newCases: Case[] = cases.map((testCase) => { + if (testCase.id === id) { + return { + id, + result: testCase.result, + testcase: { + id, + input, + output, + }, + }; + } else { + return testCase; + } + }); + updateCases(newCases); + }; + + const notify = (text: string) => { + clearTimeout(notificationTimeout!); + setNotification(text); + notificationTimeout = setTimeout(() => { + setNotification(null); + notificationTimeout = undefined; + }, 1000); + }; + + const views: JSX.Element[] = []; + cases.forEach((value, index) => { + if (focusLast && index === cases.length - 1) { + views.push( + , + ); + debounceFocusLast(); + } else { + views.push( + , + ); + } + }); - const showInfoPage = () => { - sendMessageToVSCode({ - command: 'get-ext-logs', - }); - setInfoPageVisible(true); - }; + const renderSubmitButton = () => { + if (!problem.url.startsWith('http')) { + return null; + } - const renderDonateButton = () => { - const diff = new Date().getTime() - webviewState.dialogCloseDate; - const diffInDays = diff / (1000 * 60 * 60 * 24); - if (diffInDays < 14) { - return null; - } + let url: URL; + try { + url = new URL(problem.url); + } catch (err) { + console.error(err, problem); + return null; + } + if ( + url.hostname !== 'codeforces.com' && + url.hostname !== 'open.kattis.com' + ) { + return null; + } - return ( -
- closeDonateBox()} - > - + if (url.hostname == 'codeforces.com') { + return ( + + ); + } else if (url.hostname == 'open.kattis.com') { + return ( +
+ + {waitingForSubmit && ( + <> + Submitting... +
+ + To submit to Kattis, you need to have the{' '} +
+ submission client{' '} -

🌸

-

If you find CPH useful, please consider supporting.

-

- Your contribution helps support continued development of - CPH. CPH is free and open source, thanks to your support. -

- - Donate + and the{' '} + + configuration file{' '} -
- ); - }; - - const renderInfoPage = () => { - if (infoPageVisible === false) { - return null; - } - - if (generatedJson === null) { - return ( - setInfoPageVisible(false)} - /> - ); - } - const logs = storedLogs; - const contents = ( -
- A VS Code extension to make competitive programming easier, - created by Divyanshu Agrawal -
-

🤖 Enable AI compilation

- Get 100x faster compilation using AI, please opt-in below. Your - data will be used to train cats to write JavaScript. + downloaded in a folder called .kattis in your + home directory.
+ Submission result will open in your browser.
- -
-

Get Help

- - User guide - -
-

Commit

-
{generatedJson.gitCommitHash}
-
-

Build Time

- {generatedJson.dateTime} -
-

License

-
{generatedJson.licenseString}
-
- {window.showLiveUserCount && ( - <> -

Live user count

- {liveUserCount} {liveUserCount === 1 ? 'user' : 'users'} online. -
- - )} -

UI Logs

-
{logs}
-
-

Extension Logs

-
{extLogs}
-
- ); - - return ( - setInfoPageVisible(false)} - /> - ); - }; +
+
+ + )} +
+ ); + } + }; + + const getHref = () => { + if (problem.local === undefined || problem.local === false) { + return problem.url; + } else { + return undefined; + } + }; + + const showInfoPage = () => { + sendMessageToVSCode({ + command: 'get-ext-logs', + }); + setInfoPageVisible(true); + }; + + const renderDonateButton = () => { + const diff = new Date().getTime() - webviewState.dialogCloseDate; + const diffInDays = diff / (1000 * 60 * 60 * 24); + if (diffInDays < 14) { + return null; + } return ( -
- {notification &&
{notification}
} - {renderDonateButton()} - {renderInfoPage()} -
-

- {problem.name}{' '} - {compiling && ( - - - - )} -

-
-
{views}
-
-
- - {renderSubmitButton()} -
+
+ closeDonateBox()} + > + + +

🌸

+

If you find CPH useful, please consider supporting.

+

+ Your contribution helps support continued development of + CPH. CPH is free and open source, thanks to your support. +

+ + Donate + +
+ ); + }; -
- - - - Set ONLINE_JUDGE - - -
-
- -
-

-

- {window.showLiveUserCount && liveUserCount > 0 && ( -
- {' '} - {liveUserCount} {liveUserCount === 1 ? 'user' : 'users'} online. -
- )} -
-
-
- - -
-
- - - -
-
+ const renderInfoPage = () => { + if (infoPageVisible === false) { + return null; + } - {waitingForSubmit && ( -
- Waiting for extension ... -
- - To submit to codeforces, you need to have the{' '} - - cph-submit browser extension{' '} - - installed, and a browser window open. You can change - language ID from VS Code settings. -
-
- Hint: You can also press Ctrl+Alt+S to - submit. -
-
- )} -
+ if (generatedJson === null) { + return ( + setInfoPageVisible(false)} + /> + ); + } + const logs = storedLogs; + const contents = ( +
+ A VS Code extension to make competitive programming easier, + created by Divyanshu Agrawal +
+

🤖 Enable AI compilation

+ Get 100x faster compilation using AI, please opt-in below. Your + data will be used to train cats to write JavaScript. +
+
+ +
+

Get Help

+ + User guide + +
+

Commit

+
{generatedJson.gitCommitHash}
+
+

Build Time

+ {generatedJson.dateTime} +
+

License

+
{generatedJson.licenseString}
+
+ {window.showLiveUserCount && ( + <> +

Live user count

+ {liveUserCount} {liveUserCount === 1 ? 'user' : 'users'} online. +
+ + )} +

UI Logs

+
{logs}
+
+

Extension Logs

+
{extLogs}
+
); + + return ( + setInfoPageVisible(false)} + /> + ); + }; + + return ( +
+ {notification &&
{notification}
} + {renderDonateButton()} + {renderInfoPage()} +
+

+ {problem.name}{' '} + {compiling && ( + + + + )} +

+
+
{views}
+
+
+ + {renderSubmitButton()} +
+ +
+ + + + Set ONLINE_JUDGE + + +
+
+ +
+

+

+ {window.showLiveUserCount && liveUserCount > 0 && ( +
+ {' '} + {liveUserCount} {liveUserCount === 1 ? 'user' : 'users'}{' '} online. +
+ )} +
+
+
+ + +
+
+ + + +
+
+ + {waitingForSubmit && ( +
+ Waiting for extension ... +
+ + To submit to codeforces, you need to have the{' '} + + cph-submit browser extension{' '} + + installed, and a browser window open. You can change + language ID from VS Code settings. +
+
+ Hint: You can also press Ctrl+Alt+S to + submit. +
+
+ )} +
+ ); } const getCasesFromProblem = (problem: Problem | undefined): Case[] => { - if (problem === undefined) { - return []; - } - - return problem.tests.map((testCase) => ({ - id: testCase.id, - result: null, - testcase: testCase, - })); + if (problem === undefined) { + return []; + } + + return problem.tests.map((testCase) => ({ + id: testCase.id, + result: null, + testcase: testCase, + })); }; /** @@ -755,127 +755,127 @@ const getCasesFromProblem = (problem: Problem | undefined): Case[] => { * Otherwise, shows the Judge view. */ function App() { - const [problem, setProblem] = useState(undefined); - const [cases, setCases] = useState([]); - const [deferSaveTimer, setDeferSaveTimer] = useState(null); - const [, setSaving] = useState(false); - const [showFallback, setShowFallback] = useState(false); - - // Save the problem - const save = () => { - setSaving(true); - if (problem !== undefined) { - vscodeApi.postMessage({ - command: 'save', - problem, - }); + const [problem, setProblem] = useState(undefined); + const [cases, setCases] = useState([]); + const [deferSaveTimer, setDeferSaveTimer] = useState(null); + const [, setSaving] = useState(false); + const [showFallback, setShowFallback] = useState(false); + + // Save the problem + const save = () => { + setSaving(true); + if (problem !== undefined) { + vscodeApi.postMessage({ + command: 'save', + problem, + }); + } + setTimeout(() => { + setSaving(false); + }, 500); + }; + + const handleRunSingleResult = (data: ResultCommand) => { + const idx = cases.findIndex( + (testCase) => testCase.id === data.result.id, + ); + if (idx === -1) { + console.error('Invalid single result', cases, cases.length, data); + return; + } + const newCases = cases.slice(); + newCases[idx].result = data.result; + setCases(newCases); + }; + + // Save problem if it changes. + useEffect(() => { + if (deferSaveTimer !== null) { + clearTimeout(deferSaveTimer); + } + const timeOutId = window.setTimeout(() => { + setDeferSaveTimer(null); + save(); + }, 500); + setDeferSaveTimer(timeOutId); + }, [problem]); + + useEffect(() => { + const fn = (event: any) => { + const data: VSToWebViewMessage = event.data; + switch (data.command) { + case 'new-problem': { + if (data.problem === undefined) { + setShowFallback(true); + } + + setProblem(data.problem); + setCases(getCasesFromProblem(data.problem)); + break; } - setTimeout(() => { - setSaving(false); - }, 500); - }; - - const handleRunSingleResult = (data: ResultCommand) => { - const idx = cases.findIndex( - (testCase) => testCase.id === data.result.id, - ); - if (idx === -1) { - console.error('Invalid single result', cases, cases.length, data); - return; + case 'run-single-result': { + handleRunSingleResult(data); + break; } - const newCases = cases.slice(); - newCases[idx].result = data.result; - setCases(newCases); + } }; - - // Save problem if it changes. - useEffect(() => { - if (deferSaveTimer !== null) { - clearTimeout(deferSaveTimer); - } - const timeOutId = window.setTimeout(() => { - setDeferSaveTimer(null); - save(); - }, 500); - setDeferSaveTimer(timeOutId); - }, [problem]); - - useEffect(() => { - const fn = (event: any) => { - const data: VSToWebViewMessage = event.data; - switch (data.command) { - case 'new-problem': { - if (data.problem === undefined) { - setShowFallback(true); - } - - setProblem(data.problem); - setCases(getCasesFromProblem(data.problem)); - break; - } - case 'run-single-result': { - handleRunSingleResult(data); - break; - } - } - }; - window.addEventListener('message', fn); - return () => { - window.removeEventListener('message', fn); - }; - }, [cases]); - - const createProblem = () => { - vscodeApi.postMessage({ - command: 'create-local-problem', - }); + window.addEventListener('message', fn); + return () => { + window.removeEventListener('message', fn); }; + }, [cases]); - if (problem === undefined && showFallback) { - return ( - <> -
-
-

- This document does not have a CPH problem associated - with it. -

-
-
- - - {' '} - Create Problem -
- - - - {' '} - How to use this extension - -
-
- - ); - } else if (problem !== undefined) { - return ( - - ); - } else { - return ( - <> -
Loading...
- - ); - } + const createProblem = () => { + vscodeApi.postMessage({ + command: 'create-local-problem', + }); + }; + + if (problem === undefined && showFallback) { + return ( + <> +
+
+

+ This document does not have a CPH problem associated + with it. +

+
+
+ + + {' '} + Create Problem +
+ + + + {' '} + How to use this extension + +
+
+ + ); + } else if (problem !== undefined) { + return ( + + ); + } else { + return ( + <> +
Loading...
+ + ); + } } const root = createRoot(document.getElementById('app')!);