diff --git a/packages/code-studio/src/main/AppMainContainer.tsx b/packages/code-studio/src/main/AppMainContainer.tsx index b2f88b6b20..acf9047dc1 100644 --- a/packages/code-studio/src/main/AppMainContainer.tsx +++ b/packages/code-studio/src/main/AppMainContainer.tsx @@ -39,6 +39,7 @@ import { PanelEvent, setDashboardData as setDashboardDataAction, setDashboardPluginData as setDashboardPluginDataAction, + stopListenForCreateDashboard, updateDashboardData as updateDashboardDataAction, } from '@deephaven/dashboard'; import { @@ -203,6 +204,8 @@ export class AppMainContainer extends Component< const { allDashboardData } = this.props; + this.dashboardLayouts = new Map(); + this.state = { contextActions: [ { @@ -261,6 +264,7 @@ export class AppMainContainer extends Component< componentDidMount(): void { this.initWidgets(); + this.initDashboardData(); this.startListeningForDisconnect(); window.addEventListener( @@ -283,13 +287,18 @@ export class AppMainContainer extends Component< this.deinitWidgets(); this.stopListeningForDisconnect(); + this.dashboardLayouts.forEach(layout => { + stopListenForCreateDashboard(layout.eventHub, this.handleCreateDashboard); + }); + window.removeEventListener( 'beforeunload', AppMainContainer.handleWindowBeforeUnload ); } - goldenLayout?: GoldenLayout; + /** Map from the dashboard ID to the GoldenLayout instance for that dashboard */ + dashboardLayouts: Map; importElement: RefObject; @@ -337,6 +346,28 @@ export class AppMainContainer extends Component< this.widgetListenerRemover?.(); } + initDashboardData(): void { + // TODO: #1746 We should be loading data from a dashboard storage store + // For now only the default dashboard data is stored with the workspace and set on the default dashboard + const { setDashboardPluginData, updateDashboardData, workspace } = + this.props; + const { data: workspaceData } = workspace; + const { filterSets, links, pluginDataMap } = workspaceData; + updateDashboardData(DEFAULT_DASHBOARD_ID, { + filterSets, + links, + }); + if (pluginDataMap != null) { + const pluginKeys = Object.keys(pluginDataMap); + for (let i = 0; i < pluginKeys.length; i += 1) { + const pluginId = pluginKeys[i]; + const pluginData = pluginDataMap[pluginId]; + log.debug('initDashboardData plugin data', pluginId, pluginData); + setDashboardPluginData(DEFAULT_DASHBOARD_ID, pluginId, pluginData); + } + } + } + openNotebookFromURL(): void { const { match } = this.props; const { notebookPath } = match.params; @@ -374,7 +405,9 @@ export class AppMainContainer extends Component< } emitLayoutEvent(event: string, ...args: unknown[]): void { - this.goldenLayout?.eventHub.emit(event, ...args); + const { activeTabKey } = this.state; + const layout = this.dashboardLayouts.get(activeTabKey); + layout?.eventHub.emit(event, ...args); } handleCancelResetLayoutPrompt(): void { @@ -465,16 +498,25 @@ export class AppMainContainer extends Component< const { updateWorkspaceData } = this.props; // Only save the data that is serializable/we want to persist to the workspace - const { closed, filterSets, links } = data; - updateWorkspaceData({ closed, filterSets, links }); + const { closed, filterSets, links, pluginDataMap } = data; + updateWorkspaceData({ closed, filterSets, links, pluginDataMap }); } - handleGoldenLayoutChange(goldenLayout: GoldenLayout): void { - this.goldenLayout = goldenLayout; - listenForCreateDashboard( - this.goldenLayout.eventHub, - this.handleCreateDashboard - ); + handleGoldenLayoutChange(newLayout: GoldenLayout): void { + const { activeTabKey } = this.state; + const oldLayout = this.dashboardLayouts.get(activeTabKey); + if (oldLayout === newLayout) return; + + if (oldLayout != null) { + stopListenForCreateDashboard( + oldLayout.eventHub, + this.handleCreateDashboard + ); + } + + this.dashboardLayouts.set(activeTabKey, newLayout); + + listenForCreateDashboard(newLayout.eventHub, this.handleCreateDashboard); } handleCreateDashboard({ diff --git a/packages/code-studio/src/storage/LocalWorkspaceStorage.ts b/packages/code-studio/src/storage/LocalWorkspaceStorage.ts index 9b0f0a3789..465d640c3f 100644 --- a/packages/code-studio/src/storage/LocalWorkspaceStorage.ts +++ b/packages/code-studio/src/storage/LocalWorkspaceStorage.ts @@ -137,6 +137,7 @@ export class LocalWorkspaceStorage implements WorkspaceStorage { closed: [{}], links, filterSets, + pluginDataMap: {}, }; } diff --git a/packages/dashboard/src/DashboardEvents.ts b/packages/dashboard/src/DashboardEvents.ts index 22564e867c..4b8988fd95 100644 --- a/packages/dashboard/src/DashboardEvents.ts +++ b/packages/dashboard/src/DashboardEvents.ts @@ -2,22 +2,30 @@ import type { EventHub } from '@deephaven/golden-layout'; export const CREATE_DASHBOARD = 'CREATE_DASHBOARD'; -export interface CreateDashboardPayload { +export interface CreateDashboardPayload { pluginId: string; title: string; - data: unknown; + data: T; } -export function listenForCreateDashboard( +export function stopListenForCreateDashboard( eventHub: EventHub, - handler: (p: CreateDashboardPayload) => void + handler: (p: CreateDashboardPayload) => void ): void { + eventHub.off(CREATE_DASHBOARD, handler); +} + +export function listenForCreateDashboard( + eventHub: EventHub, + handler: (p: CreateDashboardPayload) => void +): () => void { eventHub.on(CREATE_DASHBOARD, handler); + return () => stopListenForCreateDashboard(eventHub, handler); } -export function emitCreateDashboard( +export function emitCreateDashboard( eventHub: EventHub, - payload: CreateDashboardPayload + payload: CreateDashboardPayload ): void { eventHub.emit(CREATE_DASHBOARD, payload); } diff --git a/packages/dashboard/src/redux/hooks.ts b/packages/dashboard/src/redux/hooks.ts index a0cffe7c3b..935f1446e8 100644 --- a/packages/dashboard/src/redux/hooks.ts +++ b/packages/dashboard/src/redux/hooks.ts @@ -10,16 +10,17 @@ import { setDashboardPluginData } from './actions'; * @param pluginId - The ID of the plugin. * @returns A tuple containing the plugin data and a function to update the plugin data. */ -export function useDashboardPluginData( +export function useDashboardPluginData( dashboardId: string, pluginId: string -): [PluginData, (data: PluginData) => void] { +): [T, (data: T) => void] { const dispatch = useDispatch(); const data = useSelector((store: RootState) => - getPluginDataForDashboard(store, dashboardId, pluginId) + getPluginDataForDashboard(store, dashboardId, pluginId) ); const setData = useCallback( - newData => dispatch(setDashboardPluginData(dashboardId, pluginId, newData)), + (newData: T) => + dispatch(setDashboardPluginData(dashboardId, pluginId, newData)), [dashboardId, pluginId, dispatch] ); return [data, setData]; diff --git a/packages/dashboard/src/redux/selectors.ts b/packages/dashboard/src/redux/selectors.ts index 460c45615f..acdcdfd3f6 100644 --- a/packages/dashboard/src/redux/selectors.ts +++ b/packages/dashboard/src/redux/selectors.ts @@ -61,10 +61,10 @@ export const getOpenedPanelMapForDashboard = ( * @param dashboardId The dashboard ID to get data for * @returns The map of plugin IDs to data for all plugins on the dashboard */ -export const getPluginDataMapForDashboard = ( +export const getPluginDataMapForDashboard = ( store: RootState, dashboardId: string -): PluginDataMap => +): PluginDataMap => getDashboardData(store, dashboardId).pluginDataMap ?? EMPTY_OBJECT; /** @@ -73,8 +73,8 @@ export const getPluginDataMapForDashboard = ( * @param pluginId The plugin ID to get data for * @returns The plugin data */ -export const getPluginDataForDashboard = ( +export const getPluginDataForDashboard = ( store: RootState, dashboardId: string, pluginId: string -): PluginData => getPluginDataMapForDashboard(store, dashboardId)[pluginId]; +): T => getPluginDataMapForDashboard(store, dashboardId)[pluginId]; diff --git a/packages/redux/src/store.ts b/packages/redux/src/store.ts index adce3921db..8690a257d4 100644 --- a/packages/redux/src/store.ts +++ b/packages/redux/src/store.ts @@ -60,11 +60,14 @@ export interface WorkspaceSettings { } export interface WorkspaceData { + settings: WorkspaceSettings; + + // TODO: #1746 The rest of these options should not be stored with workspace data, we should have a separate DashboardStorage closed: unknown[]; filterSets: unknown[]; layoutConfig: unknown[]; links: unknown; - settings: WorkspaceSettings; + pluginDataMap: PluginDataMap; } export interface CustomizableWorkspaceData @@ -82,13 +85,16 @@ export interface Workspace { export type PluginData = unknown; -export type PluginDataMap = Record; +export type PluginDataMap = Record; -export type DashboardData = Record & { +export type DashboardData = Record< + string, + unknown +> & { title?: string; closed?: unknown[]; filterSets?: unknown[]; - pluginDataMap?: PluginDataMap; + pluginDataMap?: PluginDataMap; }; export type WorkspaceStorageLoadOptions = {