-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/show precomputed results tweaks #146
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
Changes from all commits
bd571c5
fdba1c7
c05b6f9
efa879f
cd11abb
a9b3864
3aa354c
dae5a6a
2353947
1a595f1
774e287
dd22a51
9517f3f
893bb5a
3e50510
a157b63
369421c
72092e3
29f22d1
dc5b4b2
0b84b49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,24 +5,34 @@ import { getJobStatus, addRecipe } from "./utils/firebase"; | |
| import { getFirebaseRecipe, jsonToString } from "./utils/recipeLoader"; | ||
| import { getSubmitPackingUrl, JOB_STATUS } from "./constants/aws"; | ||
| import { FIRESTORE_FIELDS } from "./constants/firebase"; | ||
| import { SIMULARIUM_EMBED_URL } from "./constants/urls"; | ||
| import { useResultUrl, useSetResultUrl } from "./state/store"; | ||
| import { | ||
| useJobId, | ||
| useJobLogs, | ||
| useOutputsDirectory, | ||
| useRunTime, | ||
| useSetJobId, | ||
| useSetJobLogs, | ||
| useSetPackingResults, | ||
| } from "./state/store"; | ||
| import PackingInput from "./components/PackingInput"; | ||
| import Viewer from "./components/Viewer"; | ||
| import StatusBar from "./components/StatusBar"; | ||
| import { EMPTY_PACKING_RESULTS } from "./state/constants"; | ||
|
|
||
| import "./App.css"; | ||
|
|
||
| const { Header, Content, Sider, Footer } = Layout; | ||
| const { Link } = Typography; | ||
|
|
||
| function App() { | ||
| const [jobId, setJobId] = useState(""); | ||
| const [jobStatus, setJobStatus] = useState(""); | ||
| const [jobLogs, setJobLogs] = useState<string>(""); | ||
| const [outputDir, setOutputDir] = useState<string>(""); | ||
| const [runTime, setRunTime] = useState<number>(0); | ||
| const resultUrl = useResultUrl(); | ||
| const setResultUrl = useSetResultUrl(); | ||
| const [jobStatus, setJobStatus] = useState<string>(""); | ||
| const setJobLogs = useSetJobLogs(); | ||
| const jobLogs = useJobLogs(); | ||
| const setJobId = useSetJobId(); | ||
| const jobId = useJobId(); | ||
| const setPackingResults = useSetPackingResults(); | ||
| const runTime = useRunTime(); | ||
| const outputDir = useOutputsDirectory(); | ||
|
|
||
| let start = 0; | ||
|
|
||
|
|
@@ -31,11 +41,7 @@ function App() { | |
| } | ||
|
|
||
| const resetState = () => { | ||
| setJobId(""); | ||
| setJobStatus(""); | ||
| setJobLogs(""); | ||
| setResultUrl(""); | ||
| setRunTime(0); | ||
| setPackingResults({ ...EMPTY_PACKING_RESULTS }); | ||
| }; | ||
|
|
||
| const recipeHasChanged = async ( | ||
|
|
@@ -136,12 +142,22 @@ function App() { | |
| } | ||
| } | ||
| const range = (Date.now() - start) / 1000; | ||
| setRunTime(range); | ||
| if (localJobStatus.status == JOB_STATUS.DONE) { | ||
| setResultUrl(localJobStatus.result_path); | ||
| setOutputDir(localJobStatus.outputs_directory); | ||
| setPackingResults({ | ||
| jobId: id, | ||
| jobLogs: "", | ||
| resultUrl: localJobStatus.result_path, | ||
| runTime: range, | ||
| outputDir: localJobStatus.outputs_directory, | ||
| }); | ||
| } else if (localJobStatus.status == JOB_STATUS.FAILED) { | ||
| setJobLogs(localJobStatus.error_message); | ||
| setPackingResults({ | ||
| jobId: id, | ||
| jobLogs: `Packing job failed: ${localJobStatus.error_message}`, | ||
| resultUrl: "", | ||
| runTime: range, | ||
| outputDir: "", | ||
| }); | ||
|
Comment on lines
+154
to
+160
|
||
| } | ||
| }; | ||
|
|
||
|
|
@@ -164,7 +180,7 @@ function App() { | |
| <PackingInput startPacking={startPacking} /> | ||
| </Sider> | ||
| <Content className="content-container"> | ||
| <Viewer resultUrl={resultUrl ? SIMULARIUM_EMBED_URL + resultUrl : ""} /> | ||
| <Viewer /> | ||
| </Content> | ||
| </Layout> | ||
| <Footer className="footer"> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,17 @@ | ||
| import { SIMULARIUM_EMBED_URL } from "../../constants/urls"; | ||
| import { useResultUrl } from "../../state/store"; | ||
| import "./style.css"; | ||
|
|
||
| interface ViewerProps { | ||
| resultUrl: string; | ||
| } | ||
|
|
||
| const Viewer = (props: ViewerProps): JSX.Element => { | ||
| const { resultUrl } = props; | ||
| const Viewer = (): JSX.Element => { | ||
| const resultUrl = useResultUrl(); | ||
| return ( | ||
| <div className="viewer-container"> | ||
| <iframe className="simularium-embed" src={resultUrl} /> | ||
| <iframe | ||
| className="simularium-embed" | ||
| src={`${SIMULARIUM_EMBED_URL}${resultUrl}`} | ||
| /> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default Viewer; | ||
| export default Viewer; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { EditableField, PackingResults } from "../types"; | ||
|
|
||
| // stable/frozen empty array to prevent re-renders | ||
| export const EMPTY_FIELDS: readonly EditableField[] = Object.freeze([]); | ||
| export const EMPTY_PACKING_RESULTS: PackingResults = Object.freeze({ | ||
| jobId: "", | ||
| jobLogs: "", | ||
| resultUrl: "", | ||
| runTime: 0, | ||
| outputDir: "", | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,10 @@ | ||
| import { create } from "zustand"; | ||
| import { subscribeWithSelector } from "zustand/middleware"; | ||
| import { get as lodashGet, set as lodashSet } from "lodash-es"; | ||
| import { PackingInputs } from "../types"; | ||
| import { PackingResults, RecipeManifest } from "../types"; | ||
| import { getFirebaseRecipe, jsonToString } from "../utils/recipeLoader"; | ||
| import { getPackingInputsDict } from "../utils/firebase"; | ||
| import { EMPTY_PACKING_RESULTS } from "./constants"; | ||
|
|
||
| export interface RecipeData { | ||
| id: string; | ||
|
|
@@ -14,8 +15,9 @@ export interface RecipeData { | |
|
|
||
| export interface RecipeState { | ||
| selectedRecipeId: string; | ||
| inputOptions: Record<string, PackingInputs>; | ||
| inputOptions: Record<string, RecipeManifest>; | ||
| recipes: Record<string, RecipeData>; | ||
| packingResults: PackingResults; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think ideally we want packingResults to be a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah I was thinking about that, maybe good to have in a separate PR
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| export interface UIState { | ||
|
|
@@ -43,7 +45,9 @@ type Actions = { | |
| recipeString: string | ||
| ) => Promise<void> | ||
| ) => Promise<void>; | ||
| setResultUrl: (url: string) => void; | ||
| setPackingResults: (results: PackingResults) => void; | ||
| setJobLogs: (logs: string) => void; | ||
| setJobId: (jobId: string) => void; | ||
| }; | ||
|
|
||
| export type RecipeStore = RecipeState & UIState & Actions; | ||
|
|
@@ -56,6 +60,7 @@ const initialState: RecipeState & UIState = { | |
| recipes: {}, | ||
| isLoading: false, | ||
| isPacking: false, | ||
| packingResults: { ...EMPTY_PACKING_RESULTS }, | ||
| }; | ||
|
|
||
| export const useRecipeStore = create<RecipeStore>()( | ||
|
|
@@ -115,6 +120,8 @@ export const useRecipeStore = create<RecipeStore>()( | |
| }, | ||
|
|
||
| selectRecipe: async (recipeId) => { | ||
| get().setPackingResults({ ...EMPTY_PACKING_RESULTS }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: part of why I froze this object was so avoid triggering re-renders if we use Won't be a big deal either way, but in a case like this the spread creates a new object, so subscribers will re-render, even if packing results were already empty before calling |
||
|
|
||
| const sel = get().inputOptions[recipeId]; | ||
| if (!sel) return; | ||
|
|
||
|
|
@@ -127,22 +134,27 @@ export const useRecipeStore = create<RecipeStore>()( | |
| } | ||
| }, | ||
|
|
||
| setPackingResults: (results: PackingResults) => { | ||
| set({ packingResults: results }); | ||
| }, | ||
|
|
||
| setResultUrl: (url: string) => { | ||
| const { inputOptions, selectedRecipeId } = get(); | ||
| const sel = inputOptions[selectedRecipeId]; | ||
| if (!sel) return; | ||
| setJobLogs: (logs: string) => { | ||
| set({ | ||
| inputOptions: { | ||
| ...get().inputOptions, | ||
| [selectedRecipeId]: { | ||
| ...sel, | ||
| result_path: url, | ||
| }, | ||
| packingResults: { | ||
| ...get().packingResults, | ||
| jobLogs: logs, | ||
| }, | ||
| }); | ||
| }, | ||
|
|
||
| setJobId: (jobId: string) => { | ||
| set({ | ||
| packingResults: { | ||
| ...get().packingResults, | ||
| jobId: jobId, | ||
| }, | ||
| }); | ||
| }, | ||
|
|
||
| updateRecipeString: (recipeId, newString) => { | ||
| set((s) => { | ||
|
|
@@ -234,12 +246,13 @@ export const useRecipeStore = create<RecipeStore>()( | |
| })) | ||
| ); | ||
|
|
||
| // tiny helpers/selectors (all derived — not stored) | ||
| // simple selectors | ||
| export const useSelectedRecipeId = () => | ||
| useRecipeStore((s) => s.selectedRecipeId); | ||
| export const useCurrentRecipeString = () => | ||
| useRecipeStore((s) => s.recipes[s.selectedRecipeId]?.currentString ?? ""); | ||
| export const useInputOptions = () => useRecipeStore((s) => s.inputOptions); | ||
|
|
||
| export const useIsLoading = () => useRecipeStore((s) => s.isLoading); | ||
| export const useIsPacking = () => useRecipeStore((s) => s.isPacking); | ||
| export const useFieldsToDisplay = () => | ||
|
|
@@ -248,8 +261,53 @@ export const useIsCurrentRecipeModified = () => | |
| useRecipeStore((s) => s.recipes[s.selectedRecipeId]?.isModified ?? false); | ||
| export const useGetOriginalValue = () => | ||
| useRecipeStore((s) => s.getOriginalValue); | ||
| export const useResultUrl = () => | ||
| useRecipeStore((s) => s.inputOptions[s.selectedRecipeId]?.result_path); | ||
| const usePackingResults = () => useRecipeStore((s) => s.packingResults); | ||
|
|
||
| // compound selectors | ||
|
|
||
| const useCurrentRecipeManifest = () => { | ||
| const selectedRecipeId = useSelectedRecipeId(); | ||
| const inputOptions = useInputOptions(); | ||
| if (!selectedRecipeId) return undefined; | ||
| return inputOptions[selectedRecipeId]; | ||
| }; | ||
| const useDefaultResultPath = () => { | ||
| const manifest = useCurrentRecipeManifest(); | ||
| return manifest?.defaultResultPath || ""; | ||
| }; | ||
|
|
||
| export const useRunTime = () => { | ||
| const results = usePackingResults(); | ||
| return results.runTime; | ||
| }; | ||
|
|
||
| export const useJobLogs = () => { | ||
| const results = usePackingResults(); | ||
| return results.jobLogs; | ||
| }; | ||
|
|
||
| export const useJobId = () => { | ||
| const results = usePackingResults(); | ||
| return results.jobId; | ||
| }; | ||
|
|
||
| export const useOutputsDirectory = () => { | ||
| const results = usePackingResults(); | ||
| return results.outputDir; | ||
| }; | ||
|
|
||
| export const useResultUrl = () => { | ||
| const results = usePackingResults(); | ||
| const currentRecipeId = useSelectedRecipeId(); | ||
| const defaultResultPath = useDefaultResultPath(); | ||
| let path = ""; | ||
| if (results.resultUrl) { | ||
| path = results.resultUrl; | ||
| } else if (currentRecipeId) { | ||
| path = defaultResultPath; | ||
| } | ||
| return path; | ||
| }; | ||
|
|
||
| // action selectors (stable identities) | ||
| export const useLoadInputOptions = () => | ||
|
|
@@ -265,4 +323,7 @@ export const useRestoreRecipeDefault = () => | |
| export const useStartPacking = () => useRecipeStore((s) => s.startPacking); | ||
| export const useGetCurrentValue = () => | ||
| useRecipeStore((s) => s.getCurrentValue); | ||
| export const useSetResultUrl = () => useRecipeStore((s) => s.setResultUrl); | ||
| export const useSetPackingResults = () => | ||
| useRecipeStore((s) => s.setPackingResults); | ||
| export const useSetJobLogs = () => useRecipeStore((s) => s.setJobLogs); | ||
| export const useSetJobId = () => useRecipeStore((s) => s.setJobId); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,11 +16,11 @@ export interface Dictionary<T> { | |
| [Key: string]: T; | ||
| } | ||
|
|
||
| export type PackingInputs = { | ||
| export type RecipeManifest = { | ||
| name?: string; | ||
| config: string; | ||
| recipe: string; | ||
| result_path?: string; | ||
| defaultResultPath?: string; | ||
| editable_fields?: EditableField[]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should use camel case here? |
||
| }; | ||
|
|
||
|
|
@@ -31,6 +31,14 @@ export type JobStatusObject = { | |
| result_path: string; | ||
| }; | ||
|
|
||
| export type PackingResults = { | ||
| jobId: string; | ||
| jobLogs: string; | ||
| resultUrl: string; | ||
| runTime: number; | ||
| outputDir: string; | ||
| }; | ||
|
|
||
| export type EditableField = { | ||
| id: string; | ||
| name: string; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.