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

Compile Model button and CompileContextProvider #191

Merged
merged 11 commits into from
Aug 1, 2024
5 changes: 4 additions & 1 deletion gui/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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 theme = createTheme();

Expand All @@ -13,7 +14,9 @@ function App() {
<ThemeProvider theme={theme}>
<div className="MainWindow">
<ProjectContextProvider>
<HomePage />
<CompileContextProvider>
<HomePage />
</CompileContextProvider>
</ProjectContextProvider>
<Analytics />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@ import { Cancel, Check } from "@mui/icons-material";
import CloseableDialog, {
useDialogControls,
} from "@SpComponents/CloseableDialog";
import { FunctionComponent, useCallback, useEffect, useState } from "react";
import {
FunctionComponent,
useCallback,
useContext,
useEffect,
useState,
} from "react";
import ConfigureCompilationServerDialog from "./ConfigureCompilationServerDialog";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import { CompileContext } from "@SpCompileContext/CompileContext";

export const publicUrl = "https://trom-stan-wasm-server.magland.org";
export const localUrl = "http://localhost:8083";
Expand All @@ -20,22 +27,8 @@ type CompilationServerConnectionControlProps = {
const CompilationServerConnectionControl: FunctionComponent<
CompilationServerConnectionControlProps
> = () => {
const [stanWasmServerUrl, setStanWasmServerUrl] = useState<string>(
localStorage.getItem("stanWasmServerUrl") || publicUrl,
);

const [serverType, setServerType] = useState<ServerType>(
stanWasmServerUrl === publicUrl
? "public"
: stanWasmServerUrl === localUrl
? "local"
: "custom",
);

const { stanWasmServerUrl } = useContext(CompileContext);
const { isConnected, retryConnection } = useIsConnected(stanWasmServerUrl);
useEffect(() => {
localStorage.setItem("stanWasmServerUrl", stanWasmServerUrl);
}, [stanWasmServerUrl]);

const {
handleOpen: openDialog,
Expand All @@ -47,6 +40,13 @@ const CompilationServerConnectionControl: FunctionComponent<
retryConnection();
}, [retryConnection]);

const serverLabel =
stanWasmServerUrl === publicUrl
? "public"
: stanWasmServerUrl === localUrl
? "local"
: "custom";

return (
<>
<IconButton onClick={openDialog} color="inherit" size="small">
Expand All @@ -58,7 +58,7 @@ const CompilationServerConnectionControl: FunctionComponent<
&nbsp;
<Typography color="white" fontSize={12}>
{isConnected ? "connected to " : "not connected to "}
{serverType}
{serverLabel}
</Typography>
</IconButton>
<CloseableDialog
Expand All @@ -68,12 +68,8 @@ const CompilationServerConnectionControl: FunctionComponent<
handleClose={closeDialog}
>
<ConfigureCompilationServerDialog
stanWasmServerUrl={stanWasmServerUrl}
setStanWasmServerUrl={setStanWasmServerUrl}
isConnected={isConnected}
onRetry={handleRetry}
choice={serverType}
setChoice={setServerType}
/>
</CloseableDialog>
</>
Expand All @@ -89,6 +85,11 @@ const useIsConnected = (stanWasmServerUrl: string) => {
}, []);
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
import { FunctionComponent, useCallback } from "react";
import {
localUrl,
publicUrl,
ServerType,
} from "./CompilationServerConnectionControl";
import FormControl from "@mui/material/FormControl";
import { CompileContext } from "@SpCompileContext/CompileContext";
import { Refresh } from "@mui/icons-material";
import Divider from "@mui/material/Divider";
import FormLabel from "@mui/material/FormLabel";
import RadioGroup from "@mui/material/RadioGroup";
import FormControl from "@mui/material/FormControl";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormLabel from "@mui/material/FormLabel";
import IconButton from "@mui/material/IconButton";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import TextField from "@mui/material/TextField";
import IconButton from "@mui/material/IconButton";
import { Refresh } from "@mui/icons-material";
import { FunctionComponent, useCallback, useContext } from "react";
import { localUrl, publicUrl } from "./CompilationServerConnectionControl";

type ServerType = "public" | "local" | "custom";

type ConfigureCompilationServerDialogProps = {
stanWasmServerUrl: string;
setStanWasmServerUrl: (url: string) => void;
isConnected: boolean;
onRetry: () => void;
choice: ServerType;
setChoice: (choice: ServerType) => void;
};

const ConfigureCompilationServerDialog: FunctionComponent<
ConfigureCompilationServerDialogProps
> = ({
stanWasmServerUrl,
setStanWasmServerUrl,
isConnected,
onRetry,
choice,
setChoice,
}) => {
> = ({ isConnected, onRetry }) => {
const { stanWasmServerUrl, setStanWasmServerUrl } =
useContext(CompileContext);

const serverType: ServerType =
stanWasmServerUrl === publicUrl
? "public"
: stanWasmServerUrl === localUrl
? "local"
: "custom";
WardBrian marked this conversation as resolved.
Show resolved Hide resolved

const makeChoice = useCallback(
(_: unknown, choice: string) => {
if (choice === "public") {
Expand All @@ -44,9 +42,8 @@ const ConfigureCompilationServerDialog: FunctionComponent<
} else {
return;
}
setChoice(choice);
},
[setChoice, setStanWasmServerUrl],
[setStanWasmServerUrl],
);

return (
Expand All @@ -73,7 +70,7 @@ const ConfigureCompilationServerDialog: FunctionComponent<
<FormLabel id="compilation-server-selection">
Compilation server
</FormLabel>
<RadioGroup value={choice} onChange={makeChoice}>
<RadioGroup value={serverType} onChange={makeChoice}>
<FormControlLabel
value="public"
control={<Radio />}
Expand All @@ -91,13 +88,13 @@ const ConfigureCompilationServerDialog: FunctionComponent<
/>
</RadioGroup>

{choice === "custom" && (
{serverType === "custom" && (
<div>
<p>
<TextField
variant="standard"
label="Custom server URL"
disabled={choice !== "custom"}
disabled={serverType !== "custom"}
value={stanWasmServerUrl}
onChange={(e) => setStanWasmServerUrl(e.target.value)}
/>
Expand All @@ -106,7 +103,7 @@ const ConfigureCompilationServerDialog: FunctionComponent<
)}
</FormControl>

{choice === "local" && (
{serverType === "local" && (
<div>
<p>
To start a local compilation server{" "}
Expand All @@ -119,7 +116,7 @@ const ConfigureCompilationServerDialog: FunctionComponent<
</div>
</div>
)}
{choice === "public" && (
{serverType === "public" && (
<div>
<p>
The public server <span className="details">({publicUrl})</span> is
Expand Down
29 changes: 29 additions & 0 deletions gui/src/app/CompileContext/CompileContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { createContext } from "react";

export type CompileStatus =
| "preparing"
| "compiling"
| "compiled"
| "failed"
| "";

type CompileContextType = {
compileStatus: CompileStatus;
compileMessage: string;
compiledMainJsUrl?: string;
validSyntax: boolean;
compile: () => void;
setValidSyntax: (valid: boolean) => void;
stanWasmServerUrl: string;
setStanWasmServerUrl: (url: string) => void;
};

export const CompileContext = createContext<CompileContextType>({
compileStatus: "",
compileMessage: "",
validSyntax: false,
compile: () => {},
setValidSyntax: () => {},
stanWasmServerUrl: "",
setStanWasmServerUrl: () => {},
});
102 changes: 102 additions & 0 deletions gui/src/app/CompileContext/CompileContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { ProjectContext } from "@SpCore/ProjectContextProvider";
import compileStanProgram from "@SpCompileContext/compileStanProgram";
import {
FunctionComponent,
PropsWithChildren,
useCallback,
useContext,
useEffect,
useState,
} from "react";
import { CompileContext, CompileStatus } from "./CompileContext";

type CompileContextProviderProps = {
// none
};

const initialStanWasmServerUrl =
localStorage.getItem("stanWasmServerUrl") ||
"https://trom-stan-wasm-server.magland.org";

export const CompileContextProvider: FunctionComponent<
PropsWithChildren<CompileContextProviderProps>
> = ({ children }) => {
const { data } = useContext(ProjectContext);
const [compileStatus, setCompileStatus] = useState<CompileStatus>("");
const [
theStanFileContentThasHasBeenCompiled,
setTheStanFileContentThasHasBeenCompiled,
] = useState<string>("");
const [compileMessage, setCompileMessage] = useState<string>("");
const [compiledMainJsUrl, setCompiledMainJsUrl] = useState<
string | undefined
>(undefined);
const [validSyntax, setValidSyntax] = useState<boolean>(false);

useEffect(() => {
// if the compiled content is not the same as the current content,
// then the state should not be compiled or failed
if (data.stanFileContent !== theStanFileContentThasHasBeenCompiled) {
if (compileStatus === "compiled" || compileStatus === "failed") {
setCompileStatus("");
setCompiledMainJsUrl("");
}
}
Comment on lines +39 to +44
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it worthwhile to do the flip side of this--such that if the stanFileContent is flipped back to equal the content that has been compiled, we revert to the "compiled" state?

I'm imagining deleting and re-adding a semicolon for instance.

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'd vote against that because (a) it would be challenging to manage that -- you see in this code the compiledMainJsUrl gets cleared (b) it's easy enough to click the compile button again, and it will be a cache hit.

}, [
data.stanFileContent,
theStanFileContentThasHasBeenCompiled,
compileStatus,
setCompiledMainJsUrl,
]);

const [stanWasmServerUrl, setStanWasmServerUrl] = useState<string>(
initialStanWasmServerUrl,
);
useEffect(() => {
// persist to local storage
localStorage.setItem("stanWasmServerUrl", stanWasmServerUrl);
}, [stanWasmServerUrl]);
Comment on lines +55 to +58
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure I understand why we need to persist this to local storage vs. it being part of the context?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

When user refreshes the page, or closes the tab and comes back later, we want this to remember the choice.

Copy link
Collaborator

Choose a reason for hiding this comment

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

(this is also the current behavior, the code just moved in this PR)


const handleCompile = useCallback(async () => {
setCompileStatus("compiling");
await new Promise((resolve) => setTimeout(resolve, 500)); // for effect
const onStatus = (msg: string) => {
setCompileMessage(msg);
};
const { mainJsUrl } = await compileStanProgram(
stanWasmServerUrl,
data.stanFileContent,
onStatus,
);

if (!mainJsUrl) {
setCompileStatus("failed");
return;
}
setCompiledMainJsUrl(mainJsUrl);
setCompileStatus("compiled");
setTheStanFileContentThasHasBeenCompiled(data.stanFileContent);
}, [
data.stanFileContent,
setCompiledMainJsUrl,
setCompileStatus,
stanWasmServerUrl,
]);

return (
<CompileContext.Provider
value={{
compileStatus,
compileMessage,
compiledMainJsUrl,
validSyntax,
compile: handleCompile,
setValidSyntax,
stanWasmServerUrl,
setStanWasmServerUrl,
}}
>
{children}
</CompileContext.Provider>
);
};
Loading