diff --git a/frontend/.dependency-cruiser.cjs b/frontend/.dependency-cruiser.cjs index bc995c20c..84084feeb 100644 --- a/frontend/.dependency-cruiser.cjs +++ b/frontend/.dependency-cruiser.cjs @@ -7,7 +7,7 @@ module.exports = { "You are trying to import from a file that is supposed to be an internal implementation detail of the framework from outside the framework folder. This is not allowed.", severity: "error", from: { - pathNot: "^((src/framework)|(src/App.tsx))", + pathNot: "^((src/framework)|(src/(App|main).tsx))", }, to: { path: "^(src/framework/internal)", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 172d1cac4..4a0e9b82f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -4,42 +4,50 @@ import { DrawerContent, LayoutElement, Workbench } from "@framework/Workbench"; import { LoginDialog } from "@framework/internal/components/LoginDialog"; import { NavBar } from "@framework/internal/components/NavBar"; import { SettingsContentPanels } from "@framework/internal/components/SettingsContentPanels"; -import { AuthProvider } from "@framework/internal/providers/AuthProvider"; -import { CustomQueryClientProvider } from "@framework/internal/providers/QueryClientProvider"; +import { useQueryClient } from "@tanstack/react-query"; +import { useSetStoreValue } from "./framework/StateStore"; import "./modules/registerAllModules"; import "./templates/registerAllTemplates"; const layout: LayoutElement[] = []; function App() { - const workbench = new Workbench(); + const workbench = React.useRef(new Workbench()); + const queryClient = useQueryClient(); + + const setLoadingEnsembleSet = useSetStoreValue(workbench.current.getGuiStateStore(), "loadingEnsembleSet"); React.useEffect(() => { - if (!workbench.loadLayoutFromLocalStorage()) { - workbench.makeLayout(layout); + if (!workbench.current.loadLayoutFromLocalStorage()) { + workbench.current.makeLayout(layout); + } + + if (workbench.current.getLayout().length === 0) { + workbench.current.getGuiStateStore().setValue("drawerContent", DrawerContent.ModulesList); } - if (workbench.getLayout().length === 0) { - workbench.getGuiStateStore().setValue("drawerContent", DrawerContent.ModulesList); + + const storedEnsembleIdents = workbench.current.maybeLoadEnsembleSetFromLocalStorage(); + if (storedEnsembleIdents) { + workbench.current.getGuiStateStore().setValue("loadingEnsembleSet", true); + workbench.current.loadAndSetupEnsembleSetInSession(queryClient, storedEnsembleIdents).then(() => { + setLoadingEnsembleSet(false); + }); } return function () { - workbench.clearLayout(); + workbench.current.clearLayout(); }; }, []); return ( - - - <> - -
- - -
- -
-
+ <> + +
+ + +
+ ); } diff --git a/frontend/src/framework/Workbench.ts b/frontend/src/framework/Workbench.ts index 8954c3390..8580a6cdd 100644 --- a/frontend/src/framework/Workbench.ts +++ b/frontend/src/framework/Workbench.ts @@ -41,6 +41,7 @@ export type LayoutElement = { export type WorkbenchGuiState = { drawerContent: DrawerContent; settingsPanelWidthInPercent: number; + loadingEnsembleSet: boolean; }; export class Workbench { @@ -61,6 +62,7 @@ export class Workbench { this._guiStateStore = new StateStore({ drawerContent: DrawerContent.ModuleSettings, settingsPanelWidthInPercent: parseFloat(localStorage.getItem("settingsPanelWidthInPercent") || "20"), + loadingEnsembleSet: false, }); this._workbenchSession = new WorkbenchSessionPrivate(); this._workbenchServices = new PrivateWorkbenchServices(this); @@ -239,6 +241,8 @@ export class Workbench { queryClient: QueryClient, specifiedEnsembleIdents: EnsembleIdent[] ): Promise { + this.storeEnsembleSetInLocalStorage(specifiedEnsembleIdents); + const ensembleIdentsToLoad: EnsembleIdent[] = []; for (const ensSpec of specifiedEnsembleIdents) { ensembleIdentsToLoad.push(new EnsembleIdent(ensSpec.getCaseUuid(), ensSpec.getEnsembleName())); @@ -252,6 +256,21 @@ export class Workbench { return this._workbenchSession.setEnsembleSet(newEnsembleSet); } + private storeEnsembleSetInLocalStorage(specifiedEnsembleIdents: EnsembleIdent[]): void { + const ensembleIdentsToStore = specifiedEnsembleIdents.map((el) => el.toString()); + localStorage.setItem("ensembleIdents", JSON.stringify(ensembleIdentsToStore)); + } + + maybeLoadEnsembleSetFromLocalStorage(): EnsembleIdent[] | null { + const ensembleIdentsString = localStorage.getItem("ensembleIdents"); + if (!ensembleIdentsString) return null; + + const ensembleIdents = JSON.parse(ensembleIdentsString) as string[]; + const ensembleIdentsParsed = ensembleIdents.map((el) => EnsembleIdent.fromString(el)); + + return ensembleIdentsParsed; + } + applyTemplate(template: Template): void { this.clearLayout(); diff --git a/frontend/src/framework/internal/components/NavBar/navBar.tsx b/frontend/src/framework/internal/components/NavBar/navBar.tsx index 9b9d22803..ccff4ee24 100644 --- a/frontend/src/framework/internal/components/NavBar/navBar.tsx +++ b/frontend/src/framework/internal/components/NavBar/navBar.tsx @@ -37,7 +37,10 @@ const NavBarDivider: React.FC = () => { export const NavBar: React.FC = (props) => { const [ensembleDialogOpen, setEnsembleDialogOpen] = React.useState(false); const [expanded, setExpanded] = React.useState(localStorage.getItem("navBarExpanded") === "true"); - const [loadingEnsembles, setLoadingEnsembles] = React.useState(false); + const [loadingEnsembleSet, setLoadingEnsembleSet] = useStoreState( + props.workbench.getGuiStateStore(), + "loadingEnsembleSet" + ); const [drawerContent, setDrawerContent] = useStoreState(props.workbench.getGuiStateStore(), "drawerContent"); const [settingsPanelWidth, setSettingsPanelWidth] = useStoreState( props.workbench.getGuiStateStore(), @@ -88,9 +91,9 @@ export const NavBar: React.FC = (props) => { const selectedEnsembleIdents = selectedEnsembles.map( (ens) => new EnsembleIdent(ens.caseUuid, ens.ensembleName) ); - setLoadingEnsembles(true); + setLoadingEnsembleSet(true); props.workbench.loadAndSetupEnsembleSetInSession(queryClient, selectedEnsembleIdents).then(() => { - setLoadingEnsembles(false); + setLoadingEnsembleSet(false); }); } } @@ -132,14 +135,14 @@ export const NavBar: React.FC = (props) => { onClick={handleEnsembleClick} className="w-full !text-slate-800 h-10" startIcon={ - selectedEnsembles.length === 0 && !loadingEnsembles ? ( + selectedEnsembles.length === 0 && !loadingEnsembleSet ? ( ) : ( ) : ( selectedEnsembles.length diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 74834ef23..413a6cca9 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,6 +1,9 @@ import React from "react"; import { createRoot } from "react-dom/client"; +import { AuthProvider } from "@framework/internal/providers/AuthProvider"; +import { CustomQueryClientProvider } from "@framework/internal/providers/QueryClientProvider"; + import App from "./App"; const container = document.getElementById("root"); @@ -13,6 +16,10 @@ const root = createRoot(container); root.render( - + + + + + );