diff --git a/src/App.tsx b/src/App.tsx
index 4782f5bc..b79b3831 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -6,6 +6,7 @@ import { getFirebaseRecipe, jsonToString } from "./utils/recipeLoader";
import { getSubmitPackingUrl, JOB_STATUS } from "./constants/aws";
import { FIRESTORE_FIELDS } from "./constants/firebase";
import {
+ useCurrentRecipeData,
useJobId,
useJobLogs,
useOutputsDirectory,
@@ -33,6 +34,7 @@ function App() {
const setPackingResults = useSetPackingResults();
const runTime = useRunTime();
const outputDir = useOutputsDirectory();
+ const edits = useCurrentRecipeData()?.edits || {};
let start = 0;
@@ -143,6 +145,7 @@ function App() {
resultUrl: localJobStatus.result_path,
runTime: range,
outputDir: localJobStatus.outputs_directory,
+ edits: edits,
});
} else if (localJobStatus.status == JOB_STATUS.FAILED) {
setPackingResults({
@@ -151,6 +154,7 @@ function App() {
resultUrl: "",
runTime: range,
outputDir: "",
+ edits: {}
});
}
};
diff --git a/src/components/PackingInput/index.tsx b/src/components/PackingInput/index.tsx
index a2de51da..6ed61bd6 100644
--- a/src/components/PackingInput/index.tsx
+++ b/src/components/PackingInput/index.tsx
@@ -9,6 +9,7 @@ import {
useCurrentRecipeObject,
useInputOptions,
useLoadInputOptions,
+ useIsLoading,
} from "../../state/store";
import Dropdown from "../Dropdown";
import JSONViewer from "../JSONViewer";
@@ -36,6 +37,7 @@ const PackingInput = (props: PackingInputProps): JSX.Element => {
const selectedRecipeId = useSelectedRecipeId();
const recipeObj = useCurrentRecipeObject();
const inputOptions = useInputOptions();
+ const isLoading = useIsLoading();
const loadInputOptions = useLoadInputOptions();
const loadAllRecipes = useLoadAllRecipes();
@@ -73,7 +75,7 @@ const PackingInput = (props: PackingInputProps): JSX.Element => {
const loadingText =
Loading...
;
// No recipe or dropdown options to load
- if (!recipeObj && !inputOptions[selectedRecipeId]) {
+ if (isLoading) {
return loadingText;
}
diff --git a/src/components/Viewer/index.tsx b/src/components/Viewer/index.tsx
index 8a2dc79d..bf103991 100644
--- a/src/components/Viewer/index.tsx
+++ b/src/components/Viewer/index.tsx
@@ -1,15 +1,65 @@
+import { useEffect, useRef, useState } from "react";
+import { LoadingOutlined } from "@ant-design/icons";
import { SIMULARIUM_EMBED_URL } from "../../constants/urls";
-import { useResultUrl } from "../../state/store";
+import { useIsLoading, useIsModified, useIsPacking, useResultUrl } from "../../state/store";
import "./style.css";
const Viewer = (): JSX.Element => {
const resultUrl = useResultUrl();
+ const isLoadingGlobally = useIsLoading();
+ const isPacking = useIsPacking();
+ const isModified = useIsModified();
+
+ const iframeSrc = resultUrl ? `${SIMULARIUM_EMBED_URL}${resultUrl}` : "";
+
+ const lastSrcRef = useRef("");
+ const [isLoadingIframe, setIsLoadingIframe] = useState(false);
+
+ useEffect(() => {
+ if (!iframeSrc) return;
+ if (iframeSrc === lastSrcRef.current) return;
+
+ lastSrcRef.current = iframeSrc;
+ setIsLoadingIframe(true);
+ }, [iframeSrc]);
+
+
+ const isLoading =
+ isLoadingGlobally || isLoadingIframe || !iframeSrc;
+
+ const overlayText = isPacking
+ ? "Running..."
+ : isLoading
+ ? "Loading..."
+ : isModified
+ ? "Re-run packing to view result"
+ : "";
+
+ const activeState = isLoading || isPacking;
+ const showOverlay = activeState || isModified;
+
return (
);
};
diff --git a/src/components/Viewer/style.css b/src/components/Viewer/style.css
index 25ca27d7..1e7cb218 100644
--- a/src/components/Viewer/style.css
+++ b/src/components/Viewer/style.css
@@ -1,10 +1,44 @@
.viewer-container {
+ background-color: black;
width: 100%;
height: 100%;
+ position: relative;
}
.simularium-embed {
+ background-color: black;
width: 100%;
height: 100%;
border: 1px solid black;
}
+
+.viewer-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #DCDDE5;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 10;
+}
+
+.overlay-content {
+ color: #989898;
+ font-size: 32px;
+ text-align: center;
+}
+
+.overlay-content.active {
+ color: #468ADE;
+}
+
+.dark-theme .viewer-overlay {
+ background-color: #2C2F38;
+}
+
+.dark-theme .overlay-content.active {
+ color: #8b91ff;
+}
diff --git a/src/state/constants.ts b/src/state/constants.ts
index 46418079..9bb1fec3 100644
--- a/src/state/constants.ts
+++ b/src/state/constants.ts
@@ -8,4 +8,5 @@ export const EMPTY_PACKING_RESULT: PackingResult = Object.freeze({
resultUrl: "",
runTime: 0,
outputDir: "",
+ edits: {},
});
diff --git a/src/state/store.ts b/src/state/store.ts
index 48382aed..2eba5b34 100644
--- a/src/state/store.ts
+++ b/src/state/store.ts
@@ -273,6 +273,20 @@ export const useFieldsToDisplay = () =>
export const useRecipes = () => useRecipeStore((s) => s.recipes);
export const usePackingResults = () => useRecipeStore((s) => s.packingResults);
+export const useIsLoading = () => {
+ const recipeObj = useCurrentRecipeData();
+ const selectedRecipeId = useSelectedRecipeId();
+ const inputOptions = useInputOptions();
+ return !recipeObj && !inputOptions[selectedRecipeId];
+};
+
+export const useIsModified = () => {
+ const recipeObj = useCurrentRecipeData();
+ const packingResults = useCurrentPackingResult();
+ if (!recipeObj || !packingResults) return false;
+ return !isEqual(recipeObj.edits, packingResults.edits);
+};
+
export const useCurrentRecipeObject = () => {
const recipe = useCurrentRecipeData();
return recipe
diff --git a/src/style/themeRoot.tsx b/src/style/themeRoot.tsx
index b835a577..3b3309b8 100644
--- a/src/style/themeRoot.tsx
+++ b/src/style/themeRoot.tsx
@@ -18,7 +18,9 @@ export function ThemeRoot({ children }: { children: React.ReactNode }) {
return (
- {children}
+
+ {children}
+
);
}
\ No newline at end of file
diff --git a/src/test/store.test.ts b/src/test/store.test.ts
index 34980ff8..24de1040 100644
--- a/src/test/store.test.ts
+++ b/src/test/store.test.ts
@@ -165,6 +165,7 @@ test("setPackingResults updates packing results for current recipe", async () =>
resultUrl: "http://example.com/result",
runTime: 120,
outputDir: "/output/dir",
+ edits: {}
};
act(() => {
diff --git a/src/types/index.ts b/src/types/index.ts
index 64f73cc1..1d427d8f 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -46,6 +46,7 @@ export type PackingResult = {
resultUrl: string;
runTime: number;
outputDir: string;
+ edits: Record;
};
export type EditableField = {