From 65da214cce576e37dd3737e26a8f3d13287f9279 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Fri, 24 Apr 2026 13:58:52 -0700 Subject: [PATCH 01/13] #vfb-240 - feat: enhance neuroglass layer URL building with error handling and folder extraction from http request --- .../src/utils/neuroglassStateConfig.js | 87 ++++++++++++++++--- 1 file changed, 73 insertions(+), 14 deletions(-) diff --git a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js index b1a670ea..c49b5862 100644 --- a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js +++ b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js @@ -21,29 +21,88 @@ const SHARED_VIEWPORT = { projectionScale: 1024, }; -function buildNeuroglassLayerUrl(protocol, baseUrl, instanceId) { - const path = instanceId; - if (protocol === 'neuroglancer-precomputed' || protocol === 'n5') { - // GCS / S3 require Neuroglancer's pipe notation - return `${baseUrl}/${path}/|${protocol}:`; - } - // HTTP fileservers use a precomputed:// prefix - return `precomputed://${baseUrl}/${path}`; -} - -// Datasource configuration for Datasource export const NEUROGLASS_DATASOURCE = { protocol: import.meta.env.NEUROGLASS_DATA_PROTOCOL, baseUrl: import.meta.env.NEUROGLASS_DATA_BASE_URL, - buildUrl(instanceId) { - return buildNeuroglassLayerUrl( + async buildUrl(instanceId) { + let instancePath = instanceId.replace( + /^VFB_(\d{4})([a-zA-Z0-9]+)$/i, + "VFB/i/$1/$2/" + ); + const layerURL = await buildNeuroglassLayerUrl( this.protocol, this.baseUrl, - instanceId, + instancePath, ); + console.log(`[NEUROGLASS_DATASOURCE] Built URL for instance ${instanceId}: ${layerURL}`); + return layerURL; }, }; +async function buildNeuroglassLayerUrl(protocol, baseUrl, instanceId) { + let path = instanceId; + + // Check if the protocol is 'neuroglancer-precomputed' or 'n5' + if (protocol === 'neuroglancer-precomputed') { + const url = `${baseUrl}/${path}`; + try { + // Fetch the folder contents of the URL + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch URL: ${url}`); + } + + // Check if the response is HTML + const contentType = response.headers.get('Content-Type'); + if (contentType && contentType.includes('text/html')) { + // Parse the HTML response + const html = await response.text(); + const folderNames = extractFolderNamesFromHtml(html); + + // Find the folder starting with VFB_ + const vfbFolder = folderNames.find((name) => name.startsWith('VFB_')); + + if (vfbFolder) { + // Check if the VFB_ folder contains a 'neuroglancer' folder + const vfbFolderUrl = `${url}/${vfbFolder}`; + const vfbResponse = await fetch(vfbFolderUrl); + if (!vfbResponse.ok) { + throw new Error(`Failed to fetch VFB folder: ${vfbFolderUrl}`); + } + + const vfbHtml = await vfbResponse.text(); + const vfbFolderNames = extractFolderNamesFromHtml(vfbHtml); + const neuroglancerFolder = vfbFolderNames.find( + (name) => name === 'neuroglancer/' + ); + + if (neuroglancerFolder) { + return `${vfbFolderUrl}/${neuroglancerFolder}/|${protocol}:`; + } + } + } else { + throw new Error(`Unexpected Content-Type: ${contentType}`); + } + } catch (error) { + console.error(`[buildNeuroglassLayerUrl] Error: ${error.message}`); + } + } else if (protocol === 'gs' || protocol === 'n5') { + return `${baseUrl}/${path}/|${protocol}:`; + } + + return `precomputed://${baseUrl}/${path}`; +} + +function extractFolderNamesFromHtml(html) { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + const links = doc.querySelectorAll('a'); // Select all tags + const folderNames = Array.from(links) + .map((link) => link.textContent.trim()) // Extract text content + .filter((name) => name && !name.startsWith('..')); // Exclude parent directory links + return folderNames; +} + // Fixed GLSL shader template : Only shaderControls.color and layer.opacity change per instance. export const LAYER_SHADER = [ '#uicontrol invlerp contrast', From 954d3647661f63310dbec336d820ef6f4cf919b6 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Fri, 24 Apr 2026 14:52:34 -0700 Subject: [PATCH 02/13] #VFB-240 - feat: update buildSingleInstanceLayer to use async URL fetching --- .../frontend/src/utils/neuroglassStateConfig.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js index c49b5862..fc88d43e 100644 --- a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js +++ b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js @@ -176,10 +176,12 @@ function normalizeContrast(inst) { } // Per-instance layer builder: converts a VFB instance into a Neuroglancer layer config. -function buildSingleInstanceLayer(inst) { +async function buildSingleInstanceLayer(inst) { + const source = await NEUROGLASS_DATASOURCE.buildUrl(inst.metadata.Id); + const layer = { type: 'image', - source: NEUROGLASS_DATASOURCE.buildUrl(inst.metadata.Id), + source: source, tab: 'rendering', opacity: alphaToOpacity(inst.color?.a), blend: 'additive', @@ -193,6 +195,8 @@ function buildSingleInstanceLayer(inst) { name: inst.metadata.Id, }; + console.log(`[buildSingleInstanceLayer] Built layer for instance ${inst.metadata.Id}:`, layer); + if (inst.visibleMesh === false) layer.visible = false; return layer; From 6c68ad74fcac43335b8a3037c81d2b8ede377030 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Fri, 24 Apr 2026 15:12:41 -0700 Subject: [PATCH 03/13] fix: correct neuroglass layer URL formatting in buildNeuroglassLayerUrl function --- .../frontend/src/utils/neuroglassStateConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js index fc88d43e..daf8f10d 100644 --- a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js +++ b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js @@ -77,7 +77,7 @@ async function buildNeuroglassLayerUrl(protocol, baseUrl, instanceId) { ); if (neuroglancerFolder) { - return `${vfbFolderUrl}/${neuroglancerFolder}/|${protocol}:`; + return `${vfbFolderUrl}/${neuroglancerFolder}|${protocol}:`; } } } else { From 27b88e0adeb112d0b63147f58b2a3ba8c7489085 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Fri, 24 Apr 2026 15:34:31 -0700 Subject: [PATCH 04/13] fix: update neuroglass layer URL formatting in buildNeuroglassLayerUrl function --- .../frontend/src/utils/neuroglassStateConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js index daf8f10d..967b79da 100644 --- a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js +++ b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js @@ -77,7 +77,7 @@ async function buildNeuroglassLayerUrl(protocol, baseUrl, instanceId) { ); if (neuroglancerFolder) { - return `${vfbFolderUrl}/${neuroglancerFolder}|${protocol}:`; + return `${vfbFolderUrl}/${neuroglancerFolder}:`; } } } else { From 655bafc3e0caffd21cf0f77ebed00cac6755e1ae Mon Sep 17 00:00:00 2001 From: jrmartin Date: Sun, 26 Apr 2026 13:45:58 -0700 Subject: [PATCH 05/13] #VFB-240 - use protocol --- .../frontend/src/utils/neuroglassStateConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js index 967b79da..daf8f10d 100644 --- a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js +++ b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js @@ -77,7 +77,7 @@ async function buildNeuroglassLayerUrl(protocol, baseUrl, instanceId) { ); if (neuroglancerFolder) { - return `${vfbFolderUrl}/${neuroglancerFolder}:`; + return `${vfbFolderUrl}/${neuroglancerFolder}|${protocol}:`; } } } else { From db2b3e0b53cf8df56adce92c0f4bcf936b930859 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 27 Apr 2026 10:41:39 -0700 Subject: [PATCH 06/13] #VFB-240 - Make building state function async --- .../src/components/NeuroglassViewer.jsx | 40 ++++++++++++++----- .../src/utils/neuroglassStateConfig.js | 27 +++++++------ 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx b/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx index b2ce5c05..1d366cff 100644 --- a/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx +++ b/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx @@ -8,6 +8,7 @@ const NEUROGLASS_URL = import.meta.env.NEUROGLASS_URL ?? ''; export default function NeuroglassViewer() { const [debouncedSrc, setDebouncedSrc] = useState(''); + const [iframeSrc, setIframeSrc] = useState(''); const allLoadedInstances = useSelector(state => state.instances?.allLoadedInstances); const focusedInstance = useSelector(state => state.instances?.focusedInstance); @@ -16,16 +17,35 @@ export default function NeuroglassViewer() { const theme = useTheme(); const isMobile = !useMediaQuery(theme.breakpoints.up('lg')); - // Rebuilds whenever instances, focused item, layout preference, or viewport size changes. - const iframeSrc = useMemo(() => { - const layout = resolveNeuroglassLayout(neuroglassView, isMobile); - const state = buildNeuroglassState( - allLoadedInstances, - focusedInstance?.metadata?.Id, - layout, - ); - if (!state || !NEUROGLASS_URL) return ''; - return `${NEUROGLASS_URL}/embed#!${encodeURIComponent(JSON.stringify(state))}`; + useEffect(() => { + let cancelled = false; + + async function buildSrc() { + const layout = resolveNeuroglassLayout(neuroglassView, isMobile); + + const state = await buildNeuroglassState( + allLoadedInstances, + focusedInstance?.metadata?.Id, + layout, + ); + + if (cancelled) return; + + if (!state || !NEUROGLASS_URL) { + setIframeSrc(''); + return; + } + + setIframeSrc( + `${NEUROGLASS_URL}/embed#!${encodeURIComponent(JSON.stringify(state))}` + ); + } + + buildSrc(); + + return () => { + cancelled = true; + }; }, [allLoadedInstances, focusedInstance?.metadata?.Id, neuroglassView, isMobile]); useEffect(() => { diff --git a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js index daf8f10d..aab6f6ba 100644 --- a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js +++ b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js @@ -77,7 +77,7 @@ async function buildNeuroglassLayerUrl(protocol, baseUrl, instanceId) { ); if (neuroglancerFolder) { - return `${vfbFolderUrl}/${neuroglancerFolder}|${protocol}:`; + return `${vfbFolderUrl}/${neuroglancerFolder}/|${protocol}:`; } } } else { @@ -201,18 +201,21 @@ async function buildSingleInstanceLayer(inst) { return layer; } -// Main state builder: converts loaded VFB instances + UI state into a Neuroglass viewer state object. -export function buildNeuroglassState(allLoadedInstances, focusedInstanceId, layout) { + +export async function buildNeuroglassState(allLoadedInstances, focusedInstanceId, layout) { const instances = allLoadedInstances || []; - const layers = instances - .filter(inst => { - if (!inst?.metadata?.Id) { - console.warn(`[buildNeuroglassState] Instance missing metadata ID:`, inst); - return false; - } - return true; - }) - .map(inst => buildSingleInstanceLayer(inst)); + + const layers = await Promise.all( + instances + .filter(inst => { + if (!inst?.metadata?.Id) { + console.warn(`[buildNeuroglassState] Instance missing metadata ID:`, inst); + return false; + } + return true; + }) + .map(inst => buildSingleInstanceLayer(inst)) + ); if (layers.length === 0) return null; From f23c6ae373a213410bc231d379f290a4e6fc3ec0 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 27 Apr 2026 10:44:24 -0700 Subject: [PATCH 07/13] #vfb-240 - remove unused useMemo ref --- .../frontend/src/components/NeuroglassViewer.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx b/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx index 1d366cff..ddf5caab 100644 --- a/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx +++ b/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect } from 'react'; import { useSelector } from 'react-redux'; import { Box, Typography, useMediaQuery } from '@mui/material'; import { useTheme } from '@mui/material/styles'; From 0d005118472f54114073e1df456c1d326c6267d4 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 27 Apr 2026 13:43:05 -0700 Subject: [PATCH 08/13] #VFB-240 - Cleanup logs and fix paths rebuilds --- .../src/components/NeuroglassViewer.jsx | 36 ++++-- .../src/utils/neuroglassStateConfig.js | 122 +++++++++++------- 2 files changed, 97 insertions(+), 61 deletions(-) diff --git a/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx b/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx index ddf5caab..6283c49b 100644 --- a/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx +++ b/applications/virtual-fly-brain/frontend/src/components/NeuroglassViewer.jsx @@ -19,32 +19,40 @@ export default function NeuroglassViewer() { useEffect(() => { let cancelled = false; - + const abortController = new AbortController(); async function buildSrc() { const layout = resolveNeuroglassLayout(neuroglassView, isMobile); + try{ + const state = await buildNeuroglassState( + allLoadedInstances, + focusedInstance?.metadata?.Id, + layout, + abortController.signal + ); - const state = await buildNeuroglassState( - allLoadedInstances, - focusedInstance?.metadata?.Id, - layout, - ); + if (cancelled || abortController.signal.aborted) return; - if (cancelled) return; + if (!state || !NEUROGLASS_URL) { + setIframeSrc(''); + return; + } - if (!state || !NEUROGLASS_URL) { - setIframeSrc(''); - return; + setIframeSrc( + `${NEUROGLASS_URL}/embed#!${encodeURIComponent(JSON.stringify(state))}` + ); + } catch (error) { + if (error?.name === 'AbortError' || abortController.signal.aborted) { + return; + } + throw error; } - - setIframeSrc( - `${NEUROGLASS_URL}/embed#!${encodeURIComponent(JSON.stringify(state))}` - ); } buildSrc(); return () => { cancelled = true; + abortController.abort(); }; }, [allLoadedInstances, focusedInstance?.metadata?.Id, neuroglassView, isMobile]); diff --git a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js index aab6f6ba..3d87e838 100644 --- a/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js +++ b/applications/virtual-fly-brain/frontend/src/utils/neuroglassStateConfig.js @@ -1,6 +1,7 @@ import { KNOWN_NG_VIEWS, NG_DEFAULT_LAYOUT, NG_DEFAULT_MOBILE_LAYOUT } from './constants'; const DEFAULT_CONTRAST_RANGE = [0, 123]; +const neuroglassLayerUrlCache = new Map(); // ─── Coordinate space shared by all VFB instances ──────────────────────────── const SHARED_VIEWPORT = { @@ -25,72 +26,101 @@ export const NEUROGLASS_DATASOURCE = { protocol: import.meta.env.NEUROGLASS_DATA_PROTOCOL, baseUrl: import.meta.env.NEUROGLASS_DATA_BASE_URL, async buildUrl(instanceId) { + if (neuroglassLayerUrlCache.has(instanceId)) { + return neuroglassLayerUrlCache.get(instanceId); + } + let instancePath = instanceId.replace( /^VFB_(\d{4})([a-zA-Z0-9]+)$/i, - "VFB/i/$1/$2/" + 'VFB/i/$1/$2/' ); - const layerURL = await buildNeuroglassLayerUrl( + + const layerURLPromise = buildNeuroglassLayerUrl( this.protocol, this.baseUrl, - instancePath, + instancePath ); - console.log(`[NEUROGLASS_DATASOURCE] Built URL for instance ${instanceId}: ${layerURL}`); - return layerURL; - }, + + neuroglassLayerUrlCache.set(instanceId, layerURLPromise); + + try { + const layerURL = await layerURLPromise; + neuroglassLayerUrlCache.set(instanceId, layerURL); + return layerURL; + } catch (error) { + neuroglassLayerUrlCache.delete(instanceId); + throw error; + } + } }; +function isObjectStoreUrl(baseUrl = '') { + return baseUrl.startsWith('gs://') || baseUrl.startsWith('s3://'); +} + +function isHttpUrl(baseUrl = '') { + return baseUrl.startsWith('http://') || baseUrl.startsWith('https://'); +} + async function buildNeuroglassLayerUrl(protocol, baseUrl, instanceId) { let path = instanceId; - // Check if the protocol is 'neuroglancer-precomputed' or 'n5' + // Check if the protocol is 'neuroglancer-precomputed' if (protocol === 'neuroglancer-precomputed') { - const url = `${baseUrl}/${path}`; - try { - // Fetch the folder contents of the URL - const response = await fetch(url); - if (!response.ok) { - throw new Error(`Failed to fetch URL: ${url}`); - } - - // Check if the response is HTML - const contentType = response.headers.get('Content-Type'); - if (contentType && contentType.includes('text/html')) { - // Parse the HTML response - const html = await response.text(); - const folderNames = extractFolderNamesFromHtml(html); - - // Find the folder starting with VFB_ - const vfbFolder = folderNames.find((name) => name.startsWith('VFB_')); - - if (vfbFolder) { - // Check if the VFB_ folder contains a 'neuroglancer' folder - const vfbFolderUrl = `${url}/${vfbFolder}`; - const vfbResponse = await fetch(vfbFolderUrl); - if (!vfbResponse.ok) { - throw new Error(`Failed to fetch VFB folder: ${vfbFolderUrl}`); - } + if (isObjectStoreUrl(baseUrl)) { + return `${baseUrl}/${path}/|${protocol}:`; + } - const vfbHtml = await vfbResponse.text(); - const vfbFolderNames = extractFolderNamesFromHtml(vfbHtml); - const neuroglancerFolder = vfbFolderNames.find( - (name) => name === 'neuroglancer/' - ); + if (isHttpUrl(baseUrl)) { + const url = `${baseUrl}/${path}`; + try { + // Fetch the folder contents of the URL + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch URL: ${url}`); + } - if (neuroglancerFolder) { - return `${vfbFolderUrl}/${neuroglancerFolder}/|${protocol}:`; + // Check if the response is HTML + const contentType = response.headers.get('Content-Type'); + if (contentType && contentType.includes('text/html')) { + // Parse the HTML response + const html = await response.text(); + const folderNames = extractFolderNamesFromHtml(html); + + // Find the folder starting with VFB_ + const vfbFolder = folderNames.find((name) => name.startsWith('VFB_')); + + if (vfbFolder) { + // Check if the VFB_ folder contains a 'neuroglancer' folder + const vfbFolderUrl = `${url}/${vfbFolder}`; + const vfbResponse = await fetch(vfbFolderUrl); + if (!vfbResponse.ok) { + throw new Error(`Failed to fetch VFB folder: ${vfbFolderUrl}`); + } + + const vfbHtml = await vfbResponse.text(); + const vfbFolderNames = extractFolderNamesFromHtml(vfbHtml); + const neuroglancerFolder = vfbFolderNames.find( + (name) => name === 'neuroglancer/' + ); + + if (neuroglancerFolder) { + return `${vfbFolderUrl}${neuroglancerFolder}|${protocol}:`; + } } + } else { + throw new Error(`Unexpected Content-Type: ${contentType}`); } - } else { - throw new Error(`Unexpected Content-Type: ${contentType}`); + } catch (error) { + console.error(`[buildNeuroglassLayerUrl] Error: ${error.message}`); } - } catch (error) { - console.error(`[buildNeuroglassLayerUrl] Error: ${error.message}`); } - } else if (protocol === 'gs' || protocol === 'n5') { return `${baseUrl}/${path}/|${protocol}:`; } - return `precomputed://${baseUrl}/${path}`; + if (protocol === 'gs' || protocol === 'n5') { + return `${baseUrl}/${path}/|${protocol}:`; + } } function extractFolderNamesFromHtml(html) { @@ -195,8 +225,6 @@ async function buildSingleInstanceLayer(inst) { name: inst.metadata.Id, }; - console.log(`[buildSingleInstanceLayer] Built layer for instance ${inst.metadata.Id}:`, layer); - if (inst.visibleMesh === false) layer.visible = false; return layer; From 84c0db72a3fc5eff6038f96e6d178fb963616bfe Mon Sep 17 00:00:00 2001 From: jrmartin Date: Tue, 28 Apr 2026 11:41:06 -0700 Subject: [PATCH 09/13] #VFB-242 - Create navigation bottoms constants, and update reference and checks to use these constants too. Fixes bug introduced by shifting navigation buttons once. --- .../frontend/src/components/Layout.jsx | 31 ++++++++++--------- .../vfbtoolbarMenuConfiguration.jsx | 9 +++--- .../frontend/src/shared/bottomNav/index.jsx | 9 +++--- .../frontend/src/shared/header/index.jsx | 9 +++--- .../src/shared/subHeader/SearchBuilder.jsx | 3 +- .../frontend/src/shared/subHeader/index.jsx | 5 +-- .../frontend/src/utils/constants.js | 8 +++++ 7 files changed, 44 insertions(+), 30 deletions(-) diff --git a/applications/virtual-fly-brain/frontend/src/components/Layout.jsx b/applications/virtual-fly-brain/frontend/src/components/Layout.jsx index 5785d70d..bbce5db1 100644 --- a/applications/virtual-fly-brain/frontend/src/components/Layout.jsx +++ b/applications/virtual-fly-brain/frontend/src/components/Layout.jsx @@ -18,6 +18,7 @@ import { removeAllInstances } from './../reducers/actions/instances'; import { Box, Button,Modal, useMediaQuery, useTheme, Typography, CircularProgress, Link } from "@mui/material"; import { activateCircuits, activateImages } from "../reducers/actions/layout"; import { widgetsIDs } from "./layout/widgets"; +import { bottomNavClearAll, bottomNavDownload, bottomNavLayers, bottomNavQuery, bottomNavSearch, bottomNavSnapshot, bottomNavUpload } from "../utils/constants"; const { secondaryBg, @@ -80,14 +81,14 @@ const MainLayout = ({ bottomNav, setBottomNav }) => { const queryComponentOpened = useSelector( state => state.globalInfo?.queryComponentOpened ); useEffect( () => { - if ( queryComponentOpened && bottomNav !== 2 ){ - setBottomNav(2) + if ( queryComponentOpened && bottomNav !== bottomNavQuery ){ + setBottomNav(bottomNavQuery) } }, [bottomNav, queryComponentOpened, setBottomNav]); // Handle Clear All functionality useEffect(() => { - if (bottomNav === 4) { + if (bottomNav === bottomNavClearAll) { if (allLoadedInstances?.length > 1) { removeAllInstances(); } @@ -97,7 +98,7 @@ const MainLayout = ({ bottomNav, setBottomNav }) => { }, [bottomNav, allLoadedInstances?.length, setBottomNav]); useEffect( () => { - if ( bottomNav === 3 ){ + if ( bottomNav === bottomNavLayers ){ const layoutManager = getLayoutManagerInstance(); if (!layoutManager.model.getNodeById("listViewerWidget").isVisible()) { const newWidget = { ...widgets[widgetsIDs.listViewerWidgetID] } @@ -252,22 +253,22 @@ const MainLayout = ({ bottomNav, setBottomNav }) => { {desktopScreen ? ( <> {tabContent} - {bottomNav === 0 && < VFBSnapshot open={true} setBottomNav={setBottomNav} />} - {bottomNav === 1 && < VFBUploader open={true} setBottomNav={setBottomNav} />} - {bottomNav === 2 && } - {bottomNav === 3 && } - {bottomNav === 6 && } + {bottomNav === bottomNavSnapshot && < VFBSnapshot open={true} setBottomNav={setBottomNav} />} + {bottomNav === bottomNavUpload && < VFBUploader open={true} setBottomNav={setBottomNav} />} + {bottomNav === bottomNavDownload && } + {bottomNav === bottomNavQuery && } + {bottomNav === bottomNavSearch && } ) : ( <> { - bottomNav != 3 && tabContent + bottomNav != bottomNavQuery && tabContent } - {bottomNav === 0 && } - {bottomNav === 1 && } - {bottomNav === 2 && } - {bottomNav === 3 && } - {bottomNav === 6 && } + {bottomNav === bottomNavSnapshot && } + {bottomNav === bottomNavUpload && } + {bottomNav === bottomNavDownload && } + {bottomNav === bottomNavQuery && } + {bottomNav === bottomNavSearch && } )} diff --git a/applications/virtual-fly-brain/frontend/src/components/configuration/VFBToolbar/vfbtoolbarMenuConfiguration.jsx b/applications/virtual-fly-brain/frontend/src/components/configuration/VFBToolbar/vfbtoolbarMenuConfiguration.jsx index df02622b..fcca0bf8 100644 --- a/applications/virtual-fly-brain/frontend/src/components/configuration/VFBToolbar/vfbtoolbarMenuConfiguration.jsx +++ b/applications/virtual-fly-brain/frontend/src/components/configuration/VFBToolbar/vfbtoolbarMenuConfiguration.jsx @@ -1,6 +1,7 @@ import React from 'react'; import vars from "../../../theme/variables"; import { widgets } from "../../layout/widgets"; +import { bottomNavDownload, bottomNavQuery, bottomNavSearch, bottomNavUpload } from '../../../utils/constants'; const { primaryFont, whiteColor, tabActiveColor, primaryBg } = vars; const ACTIONS = { @@ -199,7 +200,7 @@ export const toolbarMenu = (autoSaveLayout) => { return { icon: "fa fa-search", action: { handlerAction: ACTIONS.SHOW_COMPONENT, - parameters: [5] + parameters: [bottomNavSearch] } }, { @@ -207,7 +208,7 @@ export const toolbarMenu = (autoSaveLayout) => { return { icon: "fa fa-clipboard-question", action: { handlerAction: ACTIONS.SHOW_COMPONENT, - parameters: [2] + parameters: [bottomNavQuery] } }, { @@ -279,7 +280,7 @@ export const toolbarMenu = (autoSaveLayout) => { return { icon: "fa fa-download", action: { handlerAction: ACTIONS.SHOW_COMPONENT, - parameters: [1] + parameters: [bottomNavDownload] } }, { @@ -287,7 +288,7 @@ export const toolbarMenu = (autoSaveLayout) => { return { icon: "fa fa-upload", action: { handlerAction: ACTIONS.SHOW_COMPONENT, - parameters: [0] + parameters: [bottomNavUpload] } }, { diff --git a/applications/virtual-fly-brain/frontend/src/shared/bottomNav/index.jsx b/applications/virtual-fly-brain/frontend/src/shared/bottomNav/index.jsx index 1af07e26..f6f97aa0 100644 --- a/applications/virtual-fly-brain/frontend/src/shared/bottomNav/index.jsx +++ b/applications/virtual-fly-brain/frontend/src/shared/bottomNav/index.jsx @@ -3,6 +3,7 @@ import { Box, Button } from "@mui/material"; import React from "react"; import { ClearAll, Download, Query, Upload, Layers } from "../../icons"; import vars from "../../theme/variables"; +import { bottomNavClearAll, bottomNavDownload, bottomNavQuery, bottomNavSnapshot } from "../../utils/constants"; const { whiteColor, @@ -12,22 +13,22 @@ const { const navArr = [ { - id: 0, + id: bottomNavSnapshot, icon: Upload, name: 'Upload' }, { - id: 1, + id: bottomNavDownload, icon: Download, name: 'Download' }, { - id: 2, + id: bottomNavQuery, icon: Query, name: 'Query' }, { - id: 3, + id: bottomNavClearAll, icon: ClearAll, name: 'Clear all' }, diff --git a/applications/virtual-fly-brain/frontend/src/shared/header/index.jsx b/applications/virtual-fly-brain/frontend/src/shared/header/index.jsx index 3fb8271a..8eb9f1ca 100644 --- a/applications/virtual-fly-brain/frontend/src/shared/header/index.jsx +++ b/applications/virtual-fly-brain/frontend/src/shared/header/index.jsx @@ -17,6 +17,7 @@ import layout2 from "../../components/layout/layout2"; import layout3 from "../../components/layout/layout3"; import { WidgetStatus } from "@metacell/geppetto-meta-client/common/layout/model"; import { getLayoutManagerInstance } from "@metacell/geppetto-meta-client/common/layout/LayoutManager"; +import { bottomNavQuery } from "../../utils/constants"; const { primaryBg, headerBoxShadow } = vars; @@ -68,7 +69,7 @@ const Header = ({ setBottomNav }) => { } // Open the query component panel - setBottomNav(2); + setBottomNav(bottomNavQuery); } } @@ -143,10 +144,10 @@ const Header = ({ setBottomNav }) => { if (matchQuery?.queries?.[action?.parameters[1]]) { matchQuery.queries[action.parameters[1]].active = true; updateQueries(updatedQueries); - setBottomNav(2) + setBottomNav(bottomNavQuery) } else { getQueries(action.parameters[0], action.parameters[1]) - setBottomNav(2) + setBottomNav(bottomNavQuery) } break; } @@ -310,7 +311,7 @@ const Header = ({ setBottomNav }) => { {focusedInstance?.metadata?.Id && (focusedInstance?.metadata?.Queries?.length > 0 || queries?.find(q => q.short_form === focusedInstance.metadata.Id)) && (