From 2be6453c09ea6c0c77b88780095a62e19b4250e4 Mon Sep 17 00:00:00 2001 From: Michael Mrozek Date: Thu, 7 Dec 2023 15:23:21 -0500 Subject: [PATCH] Fix serialization of user-defined layouts to only include the node name instead of the entire node object, and filter out user-defined layouts that don't apply to the current device. Fixes T164 --- src/client/components/golden-layout.vue | 8 +++---- src/client/components/setup-layouts.vue | 3 ++- src/client/views/device.vue | 31 ++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/client/components/golden-layout.vue b/src/client/components/golden-layout.vue index 0e02356..2319be2 100644 --- a/src/client/components/golden-layout.vue +++ b/src/client/components/golden-layout.vue @@ -63,7 +63,7 @@ componentType: 'terminal', title: node.name, componentState: { - node, + node: node.name, }, }]; } @@ -78,7 +78,7 @@ componentType: 'terminal', title: node.name, componentState: { - node, + node: node.name, }, }))); } @@ -142,8 +142,8 @@ }); const gl = this.gl = new GoldenLayout(this.$el as HTMLDivElement, (container: ComponentContainer, itemConfig: ResolvedComponentItemConfig) => { - const node: Node = (itemConfig.componentState as any).node; - const term = this.getNodeTerminal(node.name); + const node: string = (itemConfig.componentState as any).node; + const term = this.getNodeTerminal(node); container.element.append(term.$el); return { component: term.$el, diff --git a/src/client/components/setup-layouts.vue b/src/client/components/setup-layouts.vue index 7d9d156..a2d55fc 100644 --- a/src/client/components/setup-layouts.vue +++ b/src/client/components/setup-layouts.vue @@ -44,7 +44,7 @@ layout: Layout | ResolvedLayoutConfig; } - const storageKey = 'layouts'; + const storageKey = 'layouts2'; export function deserialize(configLayouts: Layout[]): SerializedLayout[] { const rtn: SerializedLayout[] = JSON.parse(localStorage.getItem(storageKey) ?? '[]'); @@ -283,6 +283,7 @@ .preview { min-height: 600px; + pointer-events: none; } .buttons { diff --git a/src/client/views/device.vue b/src/client/views/device.vue index 2e000ec..eac134e 100644 --- a/src/client/views/device.vue +++ b/src/client/views/device.vue @@ -118,6 +118,7 @@ import { MetaInfo } from 'vue-meta'; import { ITheme } from 'xterm'; import { saveAs } from 'file-saver'; + import { ResolvedComponentItemConfig, ResolvedLayoutConfig, ResolvedRowOrColumnItemConfig, ResolvedStackItemConfig } from 'golden-layout'; import html2canvas from 'html2canvas'; import { appName, rootDataComputeds, unwrapPromise, PromiseResult } from '../root-data'; @@ -475,7 +476,7 @@ query: { device: this.id, }, - }).then(deserializeLayouts), layouts => this.applyDefaultLayout()); + }).then(deserializeLayouts).then(this.filterLayouts), layouts => this.applyDefaultLayout()); if(document.location.search) { const params = new URLSearchParams(document.location.search.substring(1)); @@ -561,6 +562,34 @@ (await this.getTerminal(node.name)).terminal.reset(); } }, + filterLayouts(layouts: SerializedLayout[]): SerializedLayout[] { + function* getNodeNames(children: readonly (ResolvedRowOrColumnItemConfig | ResolvedStackItemConfig | ResolvedComponentItemConfig)[]): IterableIterator { + for(const child of children) { + if(child.type === 'component') { + yield (child.componentState as any).node; + } else { + yield* getNodeNames(child.content) + } + } + } + const nodes = this.nodes; + return Array.from(function*() { + for(const layout of layouts) { + if(!layout.enabled) { + continue; + } + // Config-sourced layouts have already been filtered by the server, we only need to check user-sourced layouts + if(layout.source === 'user') { + const config = layout.layout as ResolvedLayoutConfig; + const requiredNames = Array.from(getNodeNames(config.root!.content)); + if(!requiredNames.every(name => nodes.find(node => node.name === name))) { + continue; + } + } + yield layout; + } + }()); + }, applyLayout(layout: SerializedLayout) { const layoutVue = this.$refs.layout as SbLayoutVue; layoutVue.loadLayout(layout.layout);