Skip to content

Commit 6d310a7

Browse files
authored
[Framework]: Change de-serialization order of ensemble set and dashboards to assert population of EnsembleFingerprintStore prior to module-internal fetches and use @hey-api utility functions (#1371)
1 parent e41626c commit 6d310a7

File tree

22 files changed

+461
-419
lines changed

22 files changed

+461
-419
lines changed

frontend/src/framework/internal/WorkbenchSession/PrivateWorkbenchSession.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export type WorkbenchSessionTopicPayloads = {
6767
[WorkbenchSessionTopic.ENSEMBLE_SET]: EnsembleSet;
6868
[WorkbenchSessionTopic.REALIZATION_FILTER_SET]: { filterSet: RealizationFilterSet };
6969
[PrivateWorkbenchSessionTopic.IS_ENSEMBLE_SET_LOADING]: boolean;
70-
[PrivateWorkbenchSessionTopic.ACTIVE_DASHBOARD]: Dashboard;
70+
[PrivateWorkbenchSessionTopic.ACTIVE_DASHBOARD]: Dashboard | null;
7171
[PrivateWorkbenchSessionTopic.DASHBOARDS]: Dashboard[];
7272
[PrivateWorkbenchSessionTopic.METADATA]: WorkbenchSessionMetadata;
7373
[PrivateWorkbenchSessionTopic.IS_PERSISTED]: boolean;
@@ -225,15 +225,9 @@ export class PrivateWorkbenchSession implements WorkbenchSession {
225225

226226
this.clearDashboards();
227227

228-
for (const dashboard of contentState.dashboards) {
229-
const newDashboard = new Dashboard(this._atomStoreMaster);
230-
this.registerDashboard(newDashboard);
231-
newDashboard.deserializeState(dashboard);
232-
}
233-
234-
this._settings.deserializeState(contentState.settings);
235-
this._userCreatedItems.deserializeState(contentState.userCreatedItems);
236-
228+
// We first have to load and setup the ensemble set before deserializing dashboards and modules.
229+
// This is because modules may depend on ensembles being present in the EnsembleFinterprintStore when
230+
// initiating requests to the backend.
237231
const userEnsembleSettings: UserEnsembleSetting[] = contentState.ensembleSet.regularEnsembles.map((e) => ({
238232
ensembleIdent: RegularEnsembleIdent.fromString(e.ensembleIdent),
239233
customName: e.name,
@@ -254,6 +248,17 @@ export class PrivateWorkbenchSession implements WorkbenchSession {
254248
// This has to be done after loading the ensemble set
255249
// in order to guarantee that all realization filters for the ensembles exist
256250
this._realizationFilterSet.deserializeState(contentState.ensembleRealizationFilterSet);
251+
252+
// --- Now that the ensemble set is loaded, we can deserialize dashboards and modules ---
253+
254+
for (const dashboard of contentState.dashboards) {
255+
const newDashboard = new Dashboard(this._atomStoreMaster);
256+
this.registerDashboard(newDashboard);
257+
newDashboard.deserializeState(dashboard);
258+
}
259+
260+
this._settings.deserializeState(contentState.settings);
261+
this._userCreatedItems.deserializeState(contentState.userCreatedItems);
257262
}
258263

259264
async loadAndSetupEnsembleSet(
@@ -325,13 +330,12 @@ export class PrivateWorkbenchSession implements WorkbenchSession {
325330
return snapshotGetter;
326331
}
327332

328-
getActiveDashboard(): Dashboard {
333+
getActiveDashboard(): Dashboard | null {
329334
if (!this._activeDashboardId && this._dashboards.length > 0) {
330335
this._activeDashboardId = this._dashboards[0].getId();
331336
}
332337
const found = this._dashboards.find((d) => d.getId() === this._activeDashboardId);
333-
if (!found) throw new Error("Active dashboard not found");
334-
return found;
338+
return found ?? null;
335339
}
336340

337341
getDashboards(): Dashboard[] {

frontend/src/framework/internal/WorkbenchSession/WorkbenchSessionManager.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,8 @@ export class WorkbenchSessionManager implements PublishSubscribe<WorkbenchSessio
545545

546546
this._guiMessageBroker.setState(GuiState.LeftDrawerContent, LeftDrawerContent.ModuleSettings);
547547

548-
if (session.getActiveDashboard().getLayout().length === 0) {
548+
const activeDashboard = session.getActiveDashboard();
549+
if (activeDashboard && activeDashboard.getLayout().length === 0) {
549550
this._guiMessageBroker.setState(GuiState.RightDrawerContent, RightDrawerContent.ModulesList);
550551
if (this._guiMessageBroker.getState(GuiState.RightSettingsPanelWidthInPercent) === 0) {
551552
this._guiMessageBroker.setState(GuiState.RightSettingsPanelWidthInPercent, 20);
@@ -775,9 +776,8 @@ export class WorkbenchSessionManager implements PublishSubscribe<WorkbenchSessio
775776
await this.startNewSession();
776777
} else {
777778
const activeSession = this.getActiveSession();
778-
const confirmationRequired =
779-
activeSession.getDashboards().length > 0 &&
780-
activeSession.getActiveDashboard().getModuleInstances().length > 0;
779+
const activeDashboard = activeSession.getActiveDashboard();
780+
const confirmationRequired = activeDashboard && activeDashboard.getModuleInstances().length > 0;
781781

782782
if (confirmationRequired) {
783783
const result = await ConfirmationService.confirm({

frontend/src/framework/internal/components/Content/content.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import type React from "react";
22

33
import type { Workbench } from "@framework/Workbench";
44

5-
import { ActiveDashboardBoundary } from "../ActiveDashboardBoundary";
6-
75
import { DataChannelVisualizationLayer } from "./private-components/DataChannelVisualizationLayer";
86
import { Layout } from "./private-components/layout";
97

@@ -13,11 +11,11 @@ type ContentProps = {
1311

1412
export const Content: React.FC<ContentProps> = (props) => {
1513
return (
16-
<ActiveDashboardBoundary>
14+
<>
1715
<DataChannelVisualizationLayer workbench={props.workbench} />
1816
<div className="bg-gray-300 grow">
1917
<Layout workbench={props.workbench} />
2018
</div>
21-
</ActiveDashboardBoundary>
19+
</>
2220
);
2321
};

frontend/src/framework/internal/components/Content/private-components/DataChannelVisualizationLayer/dataChannelVisualizationLayer.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ import React from "react";
22

33
import type { GuiEventPayloads } from "@framework/GuiMessageBroker";
44
import { GuiEvent, GuiState, useGuiState } from "@framework/GuiMessageBroker";
5-
import { PrivateWorkbenchSessionTopic } from "@framework/internal/WorkbenchSession/PrivateWorkbenchSession";
5+
import { useActiveDashboard } from "@framework/internal/components/ActiveDashboardBoundary";
66
import type { Workbench } from "@framework/Workbench";
77
import { useElementBoundingRect } from "@lib/hooks/useElementBoundingRect";
88
import { createPortal } from "@lib/utils/createPortal";
9-
import { usePublishSubscribeTopicValue } from "@lib/utils/PublishSubscribeDelegate";
109
import { resolveClassNames } from "@lib/utils/resolveClassNames";
1110
import type { Vec2 } from "@lib/utils/vec2";
1211

@@ -26,10 +25,7 @@ type DataChannelPath = {
2625
};
2726

2827
export const DataChannelVisualizationLayer: React.FC<DataChannelVisualizationProps> = (props) => {
29-
const dashboard = usePublishSubscribeTopicValue(
30-
props.workbench.getSessionManager().getActiveSession(),
31-
PrivateWorkbenchSessionTopic.ACTIVE_DASHBOARD,
32-
);
28+
const dashboard = useActiveDashboard();
3329
const ref = React.useRef<SVGSVGElement>(null);
3430
const [visible, setVisible] = React.useState<boolean>(false);
3531
const [originPoint, setOriginPoint] = React.useState<Vec2>({ x: 0, y: 0 });

frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/channelReceiverNodesWrapper.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import React from "react";
22

33
import type { GuiEventPayloads } from "@framework/GuiMessageBroker";
44
import { GuiEvent, GuiState, useGuiState } from "@framework/GuiMessageBroker";
5+
import { useActiveDashboard } from "@framework/internal/components/ActiveDashboardBoundary";
56
import type { ChannelReceiver } from "@framework/internal/DataChannels/ChannelReceiver";
6-
import { PrivateWorkbenchSessionTopic } from "@framework/internal/WorkbenchSession/PrivateWorkbenchSession";
77
import type { ModuleInstance } from "@framework/ModuleInstance";
88
import type { Workbench } from "@framework/Workbench";
99
import { useElementBoundingRect } from "@lib/hooks/useElementBoundingRect";
1010
import { createPortal } from "@lib/utils/createPortal";
11-
import { usePublishSubscribeTopicValue } from "@lib/utils/PublishSubscribeDelegate";
1211
import { resolveClassNames } from "@lib/utils/resolveClassNames";
1312
import type { Vec2 } from "@lib/utils/vec2";
1413

@@ -23,10 +22,7 @@ export type ChannelReceiverNodesWrapperProps = {
2322
};
2423

2524
export const ChannelReceiverNodesWrapper: React.FC<ChannelReceiverNodesWrapperProps> = (props) => {
26-
const dashboard = usePublishSubscribeTopicValue(
27-
props.workbench.getSessionManager().getActiveSession(),
28-
PrivateWorkbenchSessionTopic.ACTIVE_DASHBOARD,
29-
);
25+
const dashboard = useActiveDashboard();
3026

3127
const [visible, setVisible] = React.useState<boolean>(false);
3228
const [currentReceiver, setCurrentReceiver] = React.useState<ChannelReceiver | null>(null);

frontend/src/framework/internal/components/Content/private-components/ViewWrapper/private-components/header.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
useGuiState,
1212
useGuiValue,
1313
} from "@framework/GuiMessageBroker";
14+
import { useActiveDashboard } from "@framework/internal/components/ActiveDashboardBoundary";
1415
import { useStatusControllerStateValue } from "@framework/internal/ModuleInstanceStatusControllerInternal";
1516
import { PrivateWorkbenchSessionTopic } from "@framework/internal/WorkbenchSession/PrivateWorkbenchSession";
1617
import type { ModuleInstance } from "@framework/ModuleInstance";
@@ -40,10 +41,7 @@ export type HeaderProps = {
4041
};
4142

4243
export const Header: React.FC<HeaderProps> = (props) => {
43-
const dashboard = usePublishSubscribeTopicValue(
44-
props.workbench.getSessionManager().getActiveSession(),
45-
PrivateWorkbenchSessionTopic.ACTIVE_DASHBOARD,
46-
);
44+
const dashboard = useActiveDashboard();
4745
const isSnapshot = usePublishSubscribeTopicValue(
4846
props.workbench.getSessionManager().getActiveSession(),
4947
PrivateWorkbenchSessionTopic.IS_SNAPSHOT,
@@ -272,10 +270,7 @@ type StatusIndicatorProps = {
272270

273271
function StatusIndicator(props: StatusIndicatorProps): React.ReactNode {
274272
const guiMessageBroker = props.workbench.getGuiMessageBroker();
275-
const dashboard = usePublishSubscribeTopicValue(
276-
props.workbench.getSessionManager().getActiveSession(),
277-
PrivateWorkbenchSessionTopic.ACTIVE_DASHBOARD,
278-
);
273+
const dashboard = useActiveDashboard();
279274

280275
const isLoading = useStatusControllerStateValue(props.moduleInstance.getStatusController(), "loading");
281276
const hotStatusMessages = useStatusControllerStateValue(

frontend/src/framework/internal/components/Content/private-components/ViewWrapper/viewWrapper.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from "react";
22

33
import { GuiEvent, GuiState, LeftDrawerContent, useGuiState, useGuiValue } from "@framework/GuiMessageBroker";
4+
import { useActiveDashboard } from "@framework/internal/components/ActiveDashboardBoundary";
45
import { DashboardTopic } from "@framework/internal/Dashboard";
5-
import { PrivateWorkbenchSessionTopic } from "@framework/internal/WorkbenchSession/PrivateWorkbenchSession";
66
import type { ModuleInstance } from "@framework/ModuleInstance";
77
import type { Workbench } from "@framework/Workbench";
88
import { pointRelativeToDomRect } from "@lib/utils/geometry";
@@ -32,10 +32,7 @@ type ViewWrapperProps = {
3232
};
3333

3434
export const ViewWrapper: React.FC<ViewWrapperProps> = (props) => {
35-
const dashboard = usePublishSubscribeTopicValue(
36-
props.workbench.getSessionManager().getActiveSession(),
37-
PrivateWorkbenchSessionTopic.ACTIVE_DASHBOARD,
38-
);
35+
const dashboard = useActiveDashboard();
3936
const [prevWidth, setPrevWidth] = React.useState<number>(props.width);
4037
const [prevHeight, setPrevHeight] = React.useState<number>(props.height);
4138
const [prevX, setPrevX] = React.useState<number>(props.x);

frontend/src/framework/internal/components/EditSessionMetadataDialog/editSessionMetadataDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export function EditSessionMetadataDialog(props: EditSessionMetadataDialogProps)
121121
}, [title]);
122122

123123
const layout = hasActiveSession
124-
? (props.workbench.getSessionManager().getActiveSession().getActiveDashboard().getLayout() ?? [])
124+
? (props.workbench.getSessionManager().getActiveSession().getActiveDashboard()?.getLayout() ?? [])
125125
: [];
126126

127127
React.useEffect(

frontend/src/framework/internal/components/LeftNavBar/leftNavBar.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ import { NavBarButton, NavBarDivider } from "@lib/components/NavBarComponents";
1313
import { usePublishSubscribeTopicValue } from "@lib/utils/PublishSubscribeDelegate";
1414
import { resolveClassNames } from "@lib/utils/resolveClassNames";
1515

16+
import { useActiveDashboard } from "../ActiveDashboardBoundary";
17+
1618
type LeftNavBarProps = {
1719
workbench: Workbench;
1820
};
1921

2022
export const LeftNavBar: React.FC<LeftNavBarProps> = (props) => {
2123
const workbenchSession = props.workbench.getSessionManager().getActiveSession();
2224
const ensembleSet = usePublishSubscribeTopicValue(workbenchSession, WorkbenchSessionTopic.ENSEMBLE_SET);
23-
const dashboard = usePublishSubscribeTopicValue(workbenchSession, PrivateWorkbenchSessionTopic.ACTIVE_DASHBOARD);
25+
const dashboard = useActiveDashboard();
2426
const layout = usePublishSubscribeTopicValue(dashboard, DashboardTopic.LAYOUT);
2527
const isSnapshot = usePublishSubscribeTopicValue(workbenchSession, PrivateWorkbenchSessionTopic.IS_SNAPSHOT);
2628

frontend/src/framework/internal/components/LeftSettingsPanel/leftSettingsPanel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import { Tune } from "@mui/icons-material";
44

55
import { GuiState, LeftDrawerContent, useGuiValue } from "@framework/GuiMessageBroker";
66
import { DashboardTopic } from "@framework/internal/Dashboard";
7-
import { PrivateWorkbenchSessionTopic } from "@framework/internal/WorkbenchSession/PrivateWorkbenchSession";
87
import type { Workbench } from "@framework/Workbench";
98
import { usePublishSubscribeTopicValue } from "@lib/utils/PublishSubscribeDelegate";
109
import { resolveClassNames } from "@lib/utils/resolveClassNames";
1110

11+
import { useActiveDashboard } from "../ActiveDashboardBoundary";
12+
1213
import { ColorPaletteSettings } from "./private-components/colorPaletteSettings";
1314
import { ModuleSettings } from "./private-components/moduleSettings";
1415
import { SyncSettings } from "./private-components/syncSettings";
@@ -18,8 +19,7 @@ type LeftSettingsPanelProps = {
1819
};
1920

2021
export const LeftSettingsPanel: React.FC<LeftSettingsPanelProps> = (props) => {
21-
const workbenchSession = props.workbench.getSessionManager().getActiveSession();
22-
const dashboard = usePublishSubscribeTopicValue(workbenchSession, PrivateWorkbenchSessionTopic.ACTIVE_DASHBOARD);
22+
const dashboard = useActiveDashboard();
2323
const moduleInstances = usePublishSubscribeTopicValue(dashboard, DashboardTopic.MODULE_INSTANCES);
2424
const drawerContent = useGuiValue(props.workbench.getGuiMessageBroker(), GuiState.LeftDrawerContent);
2525
const mainRef = React.useRef<HTMLDivElement>(null);

0 commit comments

Comments
 (0)