From 31a60acafe993f69146bad4a435db76d5fdd4638 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Tue, 8 Oct 2024 14:44:56 +0000 Subject: [PATCH 1/3] Disable compile buttons if no server connection --- .../CompilationServerConnectionControl.tsx | 40 ++---------------- gui/src/app/CompileContext/CompileContext.ts | 4 ++ .../CompileContext/CompileContextProvider.tsx | 32 ++++++++++++++ gui/src/app/FileEditor/StanFileEditor.tsx | 31 +++++++------- gui/src/app/RunPanel/RunPanel.tsx | 42 ++++++++++--------- 5 files changed, 77 insertions(+), 72 deletions(-) diff --git a/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx b/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx index bdc50b5..b3dbaee 100644 --- a/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx +++ b/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx @@ -3,13 +3,7 @@ import { Cancel, Check } from "@mui/icons-material"; import CloseableDialog, { useDialogControls, } from "@SpComponents/CloseableDialog"; -import { - FunctionComponent, - useCallback, - useContext, - useEffect, - useState, -} from "react"; +import { FunctionComponent, useCallback, useContext } from "react"; import ConfigureCompilationServerDialog from "./ConfigureCompilationServerDialog"; import IconButton from "@mui/material/IconButton"; import Typography from "@mui/material/Typography"; @@ -28,8 +22,8 @@ type CompilationServerConnectionControlProps = { const CompilationServerConnectionControl: FunctionComponent< CompilationServerConnectionControlProps > = () => { - const { stanWasmServerUrl } = useContext(CompileContext); - const { isConnected, retryConnection } = useIsConnected(stanWasmServerUrl); + const { stanWasmServerUrl, isConnected, retryConnection } = + useContext(CompileContext); const { handleOpen: openDialog, @@ -80,32 +74,4 @@ export const serverTypeForUrl = (url: string): ServerType => { : "custom"; }; -const useIsConnected = (stanWasmServerUrl: string) => { - const probeUrl = `${stanWasmServerUrl}/probe`; - const [isConnected, setIsConnected] = useState(false); - const [retryCode, setRetryCode] = useState(0); - const retryConnection = useCallback(() => { - setRetryCode((r) => r + 1); - }, []); - useEffect(() => { - setIsConnected(false); - if (!probeUrl.startsWith("http://") && !probeUrl.startsWith("https://")) { - // important to do this check because otherwise fetch may succeed because - // the server of this web app may respond with success - return; - } - (async () => { - try { - const response = await fetch(probeUrl); - if (response.status === 200) { - setIsConnected(true); - } - } catch (err) { - setIsConnected(false); - } - })(); - }, [probeUrl, retryCode]); - return { isConnected, retryConnection }; -}; - export default CompilationServerConnectionControl; diff --git a/gui/src/app/CompileContext/CompileContext.ts b/gui/src/app/CompileContext/CompileContext.ts index cfd9d01..489ded2 100644 --- a/gui/src/app/CompileContext/CompileContext.ts +++ b/gui/src/app/CompileContext/CompileContext.ts @@ -16,6 +16,8 @@ type CompileContextType = { setValidSyntax: (valid: boolean) => void; stanWasmServerUrl: string; setStanWasmServerUrl: (url: string) => void; + isConnected: boolean; + retryConnection: () => void; }; export const CompileContext = createContext({ @@ -26,4 +28,6 @@ export const CompileContext = createContext({ setValidSyntax: () => {}, stanWasmServerUrl: "", setStanWasmServerUrl: () => {}, + isConnected: false, + retryConnection: () => {}, }); diff --git a/gui/src/app/CompileContext/CompileContextProvider.tsx b/gui/src/app/CompileContext/CompileContextProvider.tsx index 9ac5271..da91c52 100644 --- a/gui/src/app/CompileContext/CompileContextProvider.tsx +++ b/gui/src/app/CompileContext/CompileContextProvider.tsx @@ -15,6 +15,34 @@ type CompileContextProviderProps = { // none }; +const useIsConnected = (stanWasmServerUrl: string) => { + const probeUrl = `${stanWasmServerUrl}/probe`; + const [isConnected, setIsConnected] = useState(false); + const [retryCode, setRetryCode] = useState(0); + const retryConnection = useCallback(() => { + setRetryCode((r) => r + 1); + }, []); + useEffect(() => { + setIsConnected(false); + if (!probeUrl.startsWith("http://") && !probeUrl.startsWith("https://")) { + // important to do this check because otherwise fetch may succeed because + // the server of this web app may respond with success + return; + } + (async () => { + try { + const response = await fetch(probeUrl); + if (response.status === 200) { + setIsConnected(true); + } + } catch (err) { + setIsConnected(false); + } + })(); + }, [probeUrl, retryCode]); + return { isConnected, retryConnection }; +}; + const initialStanWasmServerUrl = localStorage.getItem("stanWasmServerUrl") || publicCompilationServerUrl; @@ -83,6 +111,8 @@ export const CompileContextProvider: FunctionComponent< stanWasmServerUrl, ]); + const { isConnected, retryConnection } = useIsConnected(stanWasmServerUrl); + return ( {children} diff --git a/gui/src/app/FileEditor/StanFileEditor.tsx b/gui/src/app/FileEditor/StanFileEditor.tsx index d8eeb30..d17608b 100644 --- a/gui/src/app/FileEditor/StanFileEditor.tsx +++ b/gui/src/app/FileEditor/StanFileEditor.tsx @@ -32,7 +32,7 @@ const StanFileEditor: FunctionComponent = ({ setEditedFileContent, ); - const { compileStatus, compileMessage, compile, validSyntax } = + const { compileStatus, compileMessage, compile, validSyntax, isConnected } = useContext(CompileContext); const hasWarnings = useMemo(() => { @@ -89,17 +89,15 @@ const StanFileEditor: FunctionComponent = ({ }); } if (editedFileContent && editedFileContent === fileContent) { - if (compileStatus !== "compiling") { - if (validSyntax) { - ret.push({ - type: "button", - tooltip: "Compile Stan model", - label: "Compile", - icon: , - onClick: compile, - color: "info.dark", - }); - } + if (compileStatus !== "compiling" && validSyntax && isConnected) { + ret.push({ + type: "button", + tooltip: "Compile Stan model", + label: "Compile", + icon: , + onClick: compile, + color: "info.dark", + }); } if (compileStatus !== "") { ret.push({ @@ -118,15 +116,16 @@ const StanFileEditor: FunctionComponent = ({ return ret; }, [ + validSyntax, editedFileContent, + hasWarnings, + readOnly, fileContent, - compile, requestFormat, - validSyntax, compileStatus, + isConnected, + compile, compileMessage, - readOnly, - hasWarnings, ]); const isCompiling = compileStatus === "compiling"; diff --git a/gui/src/app/RunPanel/RunPanel.tsx b/gui/src/app/RunPanel/RunPanel.tsx index ce1ee76..e8cd9f7 100644 --- a/gui/src/app/RunPanel/RunPanel.tsx +++ b/gui/src/app/RunPanel/RunPanel.tsx @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { FunctionComponent, useCallback, useContext } from "react"; +import { FunctionComponent, useCallback, useContext, useMemo } from "react"; import Button from "@mui/material/Button"; import { CompileContext } from "@SpCompileContext/CompileContext"; @@ -13,6 +13,7 @@ import StanSampler from "@SpStanSampler/StanSampler"; import { StanRun } from "@SpStanSampler/useStanSampler"; import CompiledRunPanel from "./CompiledRunPanel"; import CircularProgress from "@mui/material/CircularProgress"; +import Tooltip from "@mui/material/Tooltip"; type RunPanelProps = { sampler?: StanSampler; @@ -41,23 +42,36 @@ const RunPanel: FunctionComponent = ({ sampler.cancel(); }, [sampler]); - const { compile, compileStatus, validSyntax } = useContext(CompileContext); + const { compile, compileStatus, validSyntax, isConnected } = + useContext(CompileContext); const { data: projectData } = useContext(ProjectContext); + const tooltip = useMemo(() => { + if (!validSyntax) return "Syntax error"; + if (!isConnected) return "Not connected to compilation server"; + if (!projectData.stanFileContent.trim()) return "No model to compile"; + if (modelHasUnsavedChanges(projectData)) return "Model has unsaved changes"; + return ""; + }, [isConnected, projectData, validSyntax]); + if (!dataIsSaved) { return
Data not saved
; } const compileDiv = (
- + + + + +
); @@ -89,14 +103,4 @@ const RunPanel: FunctionComponent = ({ ); }; -const isCompileModelDisabled = ( - projectData: ProjectDataModel, - validSyntax: boolean, -) => { - if (!validSyntax) return true; - if (!projectData.stanFileContent.trim()) return true; - if (modelHasUnsavedChanges(projectData)) return true; - return false; -}; - export default RunPanel; From 1afdca72f97a7211b13eb27461c4649c152374dc Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Tue, 8 Oct 2024 14:49:18 +0000 Subject: [PATCH 2/3] Lint fix --- gui/src/app/RunPanel/RunPanel.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/gui/src/app/RunPanel/RunPanel.tsx b/gui/src/app/RunPanel/RunPanel.tsx index e8cd9f7..ac72d38 100644 --- a/gui/src/app/RunPanel/RunPanel.tsx +++ b/gui/src/app/RunPanel/RunPanel.tsx @@ -4,11 +4,7 @@ import { FunctionComponent, useCallback, useContext, useMemo } from "react"; import Button from "@mui/material/Button"; import { CompileContext } from "@SpCompileContext/CompileContext"; import { ProjectContext } from "@SpCore/ProjectContextProvider"; -import { - ProjectDataModel, - SamplingOpts, - modelHasUnsavedChanges, -} from "@SpCore/ProjectDataModel"; +import { SamplingOpts, modelHasUnsavedChanges } from "@SpCore/ProjectDataModel"; import StanSampler from "@SpStanSampler/StanSampler"; import { StanRun } from "@SpStanSampler/useStanSampler"; import CompiledRunPanel from "./CompiledRunPanel"; From 401865e84e00115a16046c6f38fda97340b3596c Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Tue, 8 Oct 2024 15:33:25 +0000 Subject: [PATCH 3/3] Reorganize compilation code folder structure --- gui/src/app/App.tsx | 2 +- .../Context}/CompileContext.ts | 0 .../Context}/CompileContextProvider.tsx | 6 +++--- .../Context}/compileStanProgram.ts | 0 .../Context}/mainJsUrlCache.ts | 0 .../Control/CompilationServerDialog.tsx} | 6 +++--- .../Control/CompilationServerToolbar.tsx} | 15 +++++++-------- gui/src/app/Compilation/Control/Constants.ts | 4 ++++ gui/src/app/FileEditor/StanFileEditor.tsx | 2 +- gui/src/app/RunPanel/RunPanel.tsx | 2 +- gui/src/app/Stanc/useStanc.ts | 2 +- .../HomePage/SamplingWindow/SamplingWindow.tsx | 2 +- gui/src/app/pages/HomePage/TopBar.tsx | 2 +- gui/tsconfig.json | 16 ++++++++++------ 14 files changed, 33 insertions(+), 26 deletions(-) rename gui/src/app/{CompileContext => Compilation/Context}/CompileContext.ts (100%) rename gui/src/app/{CompileContext => Compilation/Context}/CompileContextProvider.tsx (94%) rename gui/src/app/{CompileContext => Compilation/Context}/compileStanProgram.ts (100%) rename gui/src/app/{CompileContext => Compilation/Context}/mainJsUrlCache.ts (100%) rename gui/src/app/{CompilationServerConnectionControl/ConfigureCompilationServerDialog.tsx => Compilation/Control/CompilationServerDialog.tsx} (96%) rename gui/src/app/{CompilationServerConnectionControl/CompilationServerConnectionControl.tsx => Compilation/Control/CompilationServerToolbar.tsx} (84%) create mode 100644 gui/src/app/Compilation/Control/Constants.ts diff --git a/gui/src/app/App.tsx b/gui/src/app/App.tsx index 6bea45a..98854d4 100644 --- a/gui/src/app/App.tsx +++ b/gui/src/app/App.tsx @@ -1,9 +1,9 @@ +import { CompileContextProvider } from "@SpCompilation/CompileContextProvider"; import ToggleableThemeProvider from "./ToggleableThemeProvider"; import ProjectContextProvider from "@SpCore/ProjectContextProvider"; import HomePage from "@SpPages/HomePage"; import { Analytics } from "@vercel/analytics/react"; import { BrowserRouter } from "react-router-dom"; -import { CompileContextProvider } from "./CompileContext/CompileContextProvider"; const App = () => { return ( diff --git a/gui/src/app/CompileContext/CompileContext.ts b/gui/src/app/Compilation/Context/CompileContext.ts similarity index 100% rename from gui/src/app/CompileContext/CompileContext.ts rename to gui/src/app/Compilation/Context/CompileContext.ts diff --git a/gui/src/app/CompileContext/CompileContextProvider.tsx b/gui/src/app/Compilation/Context/CompileContextProvider.tsx similarity index 94% rename from gui/src/app/CompileContext/CompileContextProvider.tsx rename to gui/src/app/Compilation/Context/CompileContextProvider.tsx index da91c52..15a5ee8 100644 --- a/gui/src/app/CompileContext/CompileContextProvider.tsx +++ b/gui/src/app/Compilation/Context/CompileContextProvider.tsx @@ -1,5 +1,4 @@ import { ProjectContext } from "@SpCore/ProjectContextProvider"; -import compileStanProgram from "@SpCompileContext/compileStanProgram"; import { FunctionComponent, PropsWithChildren, @@ -8,8 +7,9 @@ import { useEffect, useState, } from "react"; -import { CompileContext, CompileStatus } from "./CompileContext"; -import { publicCompilationServerUrl } from "@SpStanc/CompilationServerConnectionControl"; +import { CompileContext, CompileStatus } from "@SpCompilation/CompileContext"; +import { publicCompilationServerUrl } from "@SpCompilation/Constants"; +import compileStanProgram from "./compileStanProgram"; type CompileContextProviderProps = { // none diff --git a/gui/src/app/CompileContext/compileStanProgram.ts b/gui/src/app/Compilation/Context/compileStanProgram.ts similarity index 100% rename from gui/src/app/CompileContext/compileStanProgram.ts rename to gui/src/app/Compilation/Context/compileStanProgram.ts diff --git a/gui/src/app/CompileContext/mainJsUrlCache.ts b/gui/src/app/Compilation/Context/mainJsUrlCache.ts similarity index 100% rename from gui/src/app/CompileContext/mainJsUrlCache.ts rename to gui/src/app/Compilation/Context/mainJsUrlCache.ts diff --git a/gui/src/app/CompilationServerConnectionControl/ConfigureCompilationServerDialog.tsx b/gui/src/app/Compilation/Control/CompilationServerDialog.tsx similarity index 96% rename from gui/src/app/CompilationServerConnectionControl/ConfigureCompilationServerDialog.tsx rename to gui/src/app/Compilation/Control/CompilationServerDialog.tsx index d6a179e..a955f1b 100644 --- a/gui/src/app/CompilationServerConnectionControl/ConfigureCompilationServerDialog.tsx +++ b/gui/src/app/Compilation/Control/CompilationServerDialog.tsx @@ -1,4 +1,4 @@ -import { CompileContext } from "@SpCompileContext/CompileContext"; +import { CompileContext } from "@SpCompilation/CompileContext"; import { Refresh } from "@mui/icons-material"; import Divider from "@mui/material/Divider"; import FormControl from "@mui/material/FormControl"; @@ -10,11 +10,11 @@ import RadioGroup from "@mui/material/RadioGroup"; import TextField from "@mui/material/TextField"; import Typography from "@mui/material/Typography"; import { FunctionComponent, useCallback, useContext } from "react"; +import { serverTypeForUrl } from "./CompilationServerToolbar"; import { localCompilationServerUrl, publicCompilationServerUrl, - serverTypeForUrl, -} from "./CompilationServerConnectionControl"; +} from "./Constants"; type ConfigureCompilationServerDialogProps = { isConnected: boolean; diff --git a/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx b/gui/src/app/Compilation/Control/CompilationServerToolbar.tsx similarity index 84% rename from gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx rename to gui/src/app/Compilation/Control/CompilationServerToolbar.tsx index b3dbaee..6d27113 100644 --- a/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx +++ b/gui/src/app/Compilation/Control/CompilationServerToolbar.tsx @@ -4,16 +4,15 @@ import CloseableDialog, { useDialogControls, } from "@SpComponents/CloseableDialog"; import { FunctionComponent, useCallback, useContext } from "react"; -import ConfigureCompilationServerDialog from "./ConfigureCompilationServerDialog"; +import ConfigureCompilationServerDialog from "./CompilationServerDialog"; import IconButton from "@mui/material/IconButton"; import Typography from "@mui/material/Typography"; -import { CompileContext } from "@SpCompileContext/CompileContext"; - -export const publicCompilationServerUrl = - "https://stan-wasm.flatironinstitute.org"; -export const localCompilationServerUrl = "http://localhost:8083"; - -type ServerType = "public" | "local" | "custom"; +import { CompileContext } from "@SpCompilation/CompileContext"; +import { + ServerType, + publicCompilationServerUrl, + localCompilationServerUrl, +} from "./Constants"; type CompilationServerConnectionControlProps = { // none diff --git a/gui/src/app/Compilation/Control/Constants.ts b/gui/src/app/Compilation/Control/Constants.ts new file mode 100644 index 0000000..678cb20 --- /dev/null +++ b/gui/src/app/Compilation/Control/Constants.ts @@ -0,0 +1,4 @@ +export const publicCompilationServerUrl = + "https://stan-wasm.flatironinstitute.org"; +export const localCompilationServerUrl = "http://localhost:8083"; +export type ServerType = "public" | "local" | "custom"; diff --git a/gui/src/app/FileEditor/StanFileEditor.tsx b/gui/src/app/FileEditor/StanFileEditor.tsx index d17608b..1182f2f 100644 --- a/gui/src/app/FileEditor/StanFileEditor.tsx +++ b/gui/src/app/FileEditor/StanFileEditor.tsx @@ -5,7 +5,7 @@ import TextEditor from "@SpComponents/TextEditor"; import { ToolbarItem } from "@SpComponents/ToolBar"; import { stancErrorsToCodeMarkers } from "@SpStanc/Linting"; import useStanc from "@SpStanc/useStanc"; -import { CompileContext } from "@SpCompileContext/CompileContext"; +import { CompileContext } from "@SpCompilation/CompileContext"; import { FunctionComponent, useContext, useMemo, useState } from "react"; type Props = { diff --git a/gui/src/app/RunPanel/RunPanel.tsx b/gui/src/app/RunPanel/RunPanel.tsx index ac72d38..442ae77 100644 --- a/gui/src/app/RunPanel/RunPanel.tsx +++ b/gui/src/app/RunPanel/RunPanel.tsx @@ -2,7 +2,7 @@ import { FunctionComponent, useCallback, useContext, useMemo } from "react"; import Button from "@mui/material/Button"; -import { CompileContext } from "@SpCompileContext/CompileContext"; +import { CompileContext } from "@SpCompilation/CompileContext"; import { ProjectContext } from "@SpCore/ProjectContextProvider"; import { SamplingOpts, modelHasUnsavedChanges } from "@SpCore/ProjectDataModel"; import StanSampler from "@SpStanSampler/StanSampler"; diff --git a/gui/src/app/Stanc/useStanc.ts b/gui/src/app/Stanc/useStanc.ts index cd24191..12162cb 100644 --- a/gui/src/app/Stanc/useStanc.ts +++ b/gui/src/app/Stanc/useStanc.ts @@ -7,7 +7,7 @@ import { useCallback, useContext, useEffect, useMemo, useState } from "react"; // https://vitejs.dev/guide/assets#importing-script-as-a-worker // https://vitejs.dev/guide/assets#importing-asset-as-url import stancWorkerURL from "@SpStanc/stancWorker?worker&url"; -import { CompileContext } from "@SpCompileContext/CompileContext"; +import { CompileContext } from "@SpCompilation/CompileContext"; const useStanc = ( modelName: string, diff --git a/gui/src/app/pages/HomePage/SamplingWindow/SamplingWindow.tsx b/gui/src/app/pages/HomePage/SamplingWindow/SamplingWindow.tsx index 78ed28b..b5aaa52 100644 --- a/gui/src/app/pages/HomePage/SamplingWindow/SamplingWindow.tsx +++ b/gui/src/app/pages/HomePage/SamplingWindow/SamplingWindow.tsx @@ -15,7 +15,7 @@ import { import AnalysisPyWindow from "@SpScripting/Analysis/AnalysisPyWindow"; import useStanSampler, { StanRun } from "@SpStanSampler/useStanSampler"; import AnalysisRWindow from "@SpScripting/Analysis/AnalysisRWindow"; -import { CompileContext } from "@SpCompileContext/CompileContext"; +import { CompileContext } from "@SpCompilation/CompileContext"; type SamplingWindowProps = { // none diff --git a/gui/src/app/pages/HomePage/TopBar.tsx b/gui/src/app/pages/HomePage/TopBar.tsx index 6099667..821f666 100644 --- a/gui/src/app/pages/HomePage/TopBar.tsx +++ b/gui/src/app/pages/HomePage/TopBar.tsx @@ -1,4 +1,4 @@ -import CompilationServerConnectionControl from "@SpStanc/CompilationServerConnectionControl"; +import CompilationServerConnectionControl from "@SpCompilation/CompilationServerToolbar"; import { Brightness7, DarkMode, Menu, QuestionMark } from "@mui/icons-material"; import AppBar from "@mui/material/AppBar"; import IconButton from "@mui/material/IconButton"; diff --git a/gui/tsconfig.json b/gui/tsconfig.json index a5db7f4..9ab7b8d 100644 --- a/gui/tsconfig.json +++ b/gui/tsconfig.json @@ -5,7 +5,6 @@ "lib": ["ES2020", "DOM", "DOM.Iterable", "webworker"], "module": "ESNext", "skipLibCheck": true, - "types": ["vite/client"], /* Bundler mode */ "moduleResolution": "bundler", @@ -14,13 +13,11 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - /* Imports */ "baseUrl": "./src", "paths": { @@ -37,14 +34,21 @@ "@SpCore/gists/*": ["app/gists/*"], /* We are playing a bit fast and loose with the distinction */ "@SpPages/*": ["app/pages/HomePage/*"], - "@SpStanc/*": ["app/Stanc/*", "app/CompilationServerConnectionControl/*"], + "@SpStanc/*": ["app/Stanc/*"], "@SpStanSampler/*": ["app/StanSampler/*"], "@SpUtil/*": ["app/util/*"], "@SpStanStats/*": ["app/util/stan_stats/*"], "@SpScripting/*": ["app/Scripting/*"], - "@SpCompileContext/*": ["app/CompileContext/*"] + "@SpCompilation/*": [ + "app/Compilation/Context/*", + "app/Compilation/Control/*" + ] } }, "include": ["src", "test"], - "references": [{ "path": "./tsconfig.node.json" }] + "references": [ + { + "path": "./tsconfig.node.json" + } + ] }