Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable compile buttons if no server connection #225

Merged
merged 3 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gui/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type CompileContextType = {
setValidSyntax: (valid: boolean) => void;
stanWasmServerUrl: string;
setStanWasmServerUrl: (url: string) => void;
isConnected: boolean;
retryConnection: () => void;
};

export const CompileContext = createContext<CompileContextType>({
Expand All @@ -26,4 +28,6 @@ export const CompileContext = createContext<CompileContextType>({
setValidSyntax: () => {},
stanWasmServerUrl: "",
setStanWasmServerUrl: () => {},
isConnected: false,
retryConnection: () => {},
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ProjectContext } from "@SpCore/ProjectContextProvider";
import compileStanProgram from "@SpCompileContext/compileStanProgram";
import {
FunctionComponent,
PropsWithChildren,
Expand All @@ -8,13 +7,42 @@ 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
};

const useIsConnected = (stanWasmServerUrl: string) => {
const probeUrl = `${stanWasmServerUrl}/probe`;
const [isConnected, setIsConnected] = useState<boolean>(false);
const [retryCode, setRetryCode] = useState<number>(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 };
};

Comment on lines +18 to +45
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't mean to open up a can of worms here by suggesting a rewrite of working code, but I'm wondering--now that this is moved into the CompileContext--whether this still needs to be a hook, or if it'd be more natural/clear to write it as just a couple functions (with the state/useState held direct on the CompileContextProvider, perhaps).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda liked the encapsulation, but it does seem a bit odd to have a custom hook which is only used in one place, with that one place being it's own hook-like-thing.

Happy to go either way

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have strong feelings about it, and it's working as is, so it's probably not worth any mental energy to re-envision it.

You can just ship it 😸

const initialStanWasmServerUrl =
localStorage.getItem("stanWasmServerUrl") || publicCompilationServerUrl;

Expand Down Expand Up @@ -83,6 +111,8 @@ export const CompileContextProvider: FunctionComponent<
stanWasmServerUrl,
]);

const { isConnected, retryConnection } = useIsConnected(stanWasmServerUrl);

return (
<CompileContext.Provider
value={{
Expand All @@ -94,6 +124,8 @@ export const CompileContextProvider: FunctionComponent<
setValidSyntax,
stanWasmServerUrl,
setStanWasmServerUrl,
isConnected,
retryConnection,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,16 @@ import { Cancel, Check } from "@mui/icons-material";
import CloseableDialog, {
useDialogControls,
} from "@SpComponents/CloseableDialog";
import {
FunctionComponent,
useCallback,
useContext,
useEffect,
useState,
} from "react";
import ConfigureCompilationServerDialog from "./ConfigureCompilationServerDialog";
import { FunctionComponent, useCallback, useContext } from "react";
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
Expand All @@ -28,8 +21,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,
Expand Down Expand Up @@ -80,32 +73,4 @@ export const serverTypeForUrl = (url: string): ServerType => {
: "custom";
};

const useIsConnected = (stanWasmServerUrl: string) => {
const probeUrl = `${stanWasmServerUrl}/probe`;
const [isConnected, setIsConnected] = useState<boolean>(false);
const [retryCode, setRetryCode] = useState<number>(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;
4 changes: 4 additions & 0 deletions gui/src/app/Compilation/Control/Constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const publicCompilationServerUrl =
"https://stan-wasm.flatironinstitute.org";
export const localCompilationServerUrl = "http://localhost:8083";
export type ServerType = "public" | "local" | "custom";
33 changes: 16 additions & 17 deletions gui/src/app/FileEditor/StanFileEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -32,7 +32,7 @@ const StanFileEditor: FunctionComponent<Props> = ({
setEditedFileContent,
);

const { compileStatus, compileMessage, compile, validSyntax } =
const { compileStatus, compileMessage, compile, validSyntax, isConnected } =
useContext(CompileContext);

const hasWarnings = useMemo(() => {
Expand Down Expand Up @@ -89,17 +89,15 @@ const StanFileEditor: FunctionComponent<Props> = ({
});
}
if (editedFileContent && editedFileContent === fileContent) {
if (compileStatus !== "compiling") {
if (validSyntax) {
ret.push({
type: "button",
tooltip: "Compile Stan model",
label: "Compile",
icon: <Settings />,
onClick: compile,
color: "info.dark",
});
}
if (compileStatus !== "compiling" && validSyntax && isConnected) {
ret.push({
type: "button",
tooltip: "Compile Stan model",
label: "Compile",
icon: <Settings />,
onClick: compile,
color: "info.dark",
});
}
if (compileStatus !== "") {
ret.push({
Expand All @@ -118,15 +116,16 @@ const StanFileEditor: FunctionComponent<Props> = ({

return ret;
}, [
validSyntax,
editedFileContent,
hasWarnings,
readOnly,
fileContent,
compile,
requestFormat,
validSyntax,
compileStatus,
isConnected,
compile,
compileMessage,
readOnly,
hasWarnings,
]);

const isCompiling = compileStatus === "compiling";
Expand Down
50 changes: 25 additions & 25 deletions gui/src/app/RunPanel/RunPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
/* 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";
import { CompileContext } from "@SpCompilation/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";
import CircularProgress from "@mui/material/CircularProgress";
import Tooltip from "@mui/material/Tooltip";

type RunPanelProps = {
sampler?: StanSampler;
Expand Down Expand Up @@ -41,23 +38,36 @@ const RunPanel: FunctionComponent<RunPanelProps> = ({
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 <div className="RunPanelPadded">Data not saved</div>;
}

const compileDiv = (
<div>
<Button
variant="contained"
onClick={compile}
disabled={isCompileModelDisabled(projectData, validSyntax)}
>
compile model
</Button>
<Tooltip title={tooltip}>
<span>
<Button
variant="contained"
onClick={compile}
disabled={tooltip != ""}
>
compile model
</Button>
</span>
</Tooltip>
</div>
);

Expand Down Expand Up @@ -89,14 +99,4 @@ const RunPanel: FunctionComponent<RunPanelProps> = ({
);
};

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;
2 changes: 1 addition & 1 deletion gui/src/app/Stanc/useStanc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion gui/src/app/pages/HomePage/TopBar.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
Loading