From 0026dc0cca807adbd9385a4ac8a10769593624bf Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 23 Apr 2026 17:32:36 -0700 Subject: [PATCH 1/2] #VFB-241- feat: implement misalignment handling in TerminfoSlider component --- .../src/components/TermInfo/TerminfoSlider.jsx | 18 +++++++++++++++++- .../frontend/src/reducers/GlobalReducer.js | 5 +++-- .../frontend/src/reducers/InstancesReducer.js | 1 + .../frontend/src/reducers/actions/globals.js | 4 ++-- .../frontend/src/reducers/actions/instances.js | 7 ++++--- .../middleware/urlUpdaterMiddleware.js | 6 +++--- .../frontend/src/shared/subHeader/index.jsx | 8 +++++++- 7 files changed, 37 insertions(+), 12 deletions(-) diff --git a/applications/virtual-fly-brain/frontend/src/components/TermInfo/TerminfoSlider.jsx b/applications/virtual-fly-brain/frontend/src/components/TermInfo/TerminfoSlider.jsx index 418b94df..d23debee 100644 --- a/applications/virtual-fly-brain/frontend/src/components/TermInfo/TerminfoSlider.jsx +++ b/applications/virtual-fly-brain/frontend/src/components/TermInfo/TerminfoSlider.jsx @@ -7,7 +7,7 @@ import { useSelector } from 'react-redux'; import { Slide } from 'react-slideshow-image'; import { ChevronLeft, FullScreen } from '../../icons'; import { Box, Button, Typography } from '@mui/material'; -import { getInstanceByID } from '../../reducers/actions/instances'; +import { getInstanceByID, clearUrlLoadingState } from '../../reducers/actions/instances'; import Modal from '../../shared/modal/Modal'; import 'react-slideshow-image/dist/styles.css' @@ -115,12 +115,16 @@ const TerminfoSlider = (props) => { }); const [isLoading, setIsLoading] = useState(false); const reduxState = useSelector(state => state); + const misalignedTemplate = useSelector(state => state.globalInfo.misalignedTemplate) + const alignedTemplates = useSelector(state => state.globalInfo.alignedTemplates) + const misalignedIDs = useSelector(state => state.globalInfo.misalignedIDs) const handleConfirmLoad = async () => { if (!confirmationModal.example) return; setIsLoading(true); try { + clearUrlLoadingState() window.open( window.location.origin + '/?id=' + confirmationModal.example.template + '&i=' + confirmationModal.example.id, '_blank' @@ -159,6 +163,18 @@ const TerminfoSlider = (props) => { } } + + useEffect(() => { + if ( misalignedTemplate && !alignedTemplates ){ + setConfirmationModal({ + open: true, + example: { id : Object.keys(misalignedIDs) , template: misalignedTemplate }, + message: `The image you requested is aligned to another template. Click Load Template to open it in a new tab or Cancel to just view the image metadata.` + }); + } + }, [misalignedTemplate, misalignedIDs]) + + useEffect( () => { if(props?.examples) { const images = []; diff --git a/applications/virtual-fly-brain/frontend/src/reducers/GlobalReducer.js b/applications/virtual-fly-brain/frontend/src/reducers/GlobalReducer.js index 0209cf17..29489010 100644 --- a/applications/virtual-fly-brain/frontend/src/reducers/GlobalReducer.js +++ b/applications/virtual-fly-brain/frontend/src/reducers/GlobalReducer.js @@ -36,10 +36,11 @@ const GlobalReducer = (state = initialStateGlobalReducer, response) => { }) case getGlobalTypes.ALIGN_TEMPLATES:{ const aligned = response.payload.aligned; - const id = response.payload.templateID + const templateID = response.payload.templateID + const id = response.payload.id return Object.assign({}, state, { alignedTemplates: aligned, - misalignedTemplate : id, + misalignedTemplate : templateID, misalignedIDs : {...state.misalignedIDs, [id] : id } }) } diff --git a/applications/virtual-fly-brain/frontend/src/reducers/InstancesReducer.js b/applications/virtual-fly-brain/frontend/src/reducers/InstancesReducer.js index 5a46876a..087d4176 100644 --- a/applications/virtual-fly-brain/frontend/src/reducers/InstancesReducer.js +++ b/applications/virtual-fly-brain/frontend/src/reducers/InstancesReducer.js @@ -762,6 +762,7 @@ const InstancesReducer = (state = initialStateInstancesReducer, response) => { case getInstancesTypes.CLEAR_URL_LOADING_STATE: { return Object.assign({}, state, { isLoadingFromUrl: false, + isLoading: false, }); } default: diff --git a/applications/virtual-fly-brain/frontend/src/reducers/actions/globals.js b/applications/virtual-fly-brain/frontend/src/reducers/actions/globals.js index 6256f121..2d96be83 100644 --- a/applications/virtual-fly-brain/frontend/src/reducers/actions/globals.js +++ b/applications/virtual-fly-brain/frontend/src/reducers/actions/globals.js @@ -36,9 +36,9 @@ export const setFirstIDLoaded = () => ({ payload : {} }) -export const setAlignTemplates = (aligned, templateID) => ({ +export const setAlignTemplates = (aligned, id, templateID) => ({ type: getGlobalTypes.ALIGN_TEMPLATES, - payload : { aligned, templateID } + payload : { aligned, id, templateID } }) export const setTemplateID = (id) => ({ diff --git a/applications/virtual-fly-brain/frontend/src/reducers/actions/instances.js b/applications/virtual-fly-brain/frontend/src/reducers/actions/instances.js index 70a5e118..80097c3a 100644 --- a/applications/virtual-fly-brain/frontend/src/reducers/actions/instances.js +++ b/applications/virtual-fly-brain/frontend/src/reducers/actions/instances.js @@ -175,9 +175,10 @@ export const setBulkLoadingCount = (count, isFromUrl = false) => ({ payload: { count, isFromUrl } }); -export const clearUrlLoadingState = () => ({ - type: getInstancesTypes.CLEAR_URL_LOADING_STATE -}); +export const clearUrlLoadingState = () => { + store.dispatch({type: getInstancesTypes.CLEAR_URL_LOADING_STATE}); + return; +}; export const resetBulkLoading = () => ({ type: getInstancesTypes.RESET_BULK_LOADING diff --git a/applications/virtual-fly-brain/frontend/src/reducers/middleware/urlUpdaterMiddleware.js b/applications/virtual-fly-brain/frontend/src/reducers/middleware/urlUpdaterMiddleware.js index d99b21dd..6c43bf1c 100644 --- a/applications/virtual-fly-brain/frontend/src/reducers/middleware/urlUpdaterMiddleware.js +++ b/applications/virtual-fly-brain/frontend/src/reducers/middleware/urlUpdaterMiddleware.js @@ -182,14 +182,14 @@ export const urlUpdaterMiddleware = store => next => (action) => { await focusInstance(pendingFocusId); await selectInstance(pendingFocusId); // Clear URL loading state after async operations complete - store.dispatch(clearUrlLoadingState()); + clearUrlLoadingState(); // Clear the initial focus ID after a longer delay to allow all pending template operations to complete setTimeout(() => { initialUrlFocusId = null; }, 1000); })(); } else { - store.dispatch(clearUrlLoadingState()); + clearUrlLoadingState(); } } @@ -272,7 +272,7 @@ export const urlUpdaterMiddleware = store => next => (action) => { return; } // If the individual is not aligned with the current template, we need to show the misalignment dialog - store.dispatch(setAlignTemplates(false, action.payload.Id)); + store.dispatch(setAlignTemplates(false, action.payload.Id, Object.keys(action.payload?.Images)|| Object.keys(action.payload?.Examples || {}))); return; } } diff --git a/applications/virtual-fly-brain/frontend/src/shared/subHeader/index.jsx b/applications/virtual-fly-brain/frontend/src/shared/subHeader/index.jsx index 6780c02a..b619e7d5 100644 --- a/applications/virtual-fly-brain/frontend/src/shared/subHeader/index.jsx +++ b/applications/virtual-fly-brain/frontend/src/shared/subHeader/index.jsx @@ -79,6 +79,7 @@ const SubHeader = ({ setBottomNav, bottomNav }) => { const [filterOpened, setFilterOpened] = useState(false); const [selectedFilters, setSelectedFilters] = useState({}); const [desktop, setDesktop] = useState(window.innerWidth >= 1200); + const [isLoading, setIsLoading] = useState(false); useEffect(() => { const handleResize = () => { @@ -89,7 +90,6 @@ const SubHeader = ({ setBottomNav, bottomNav }) => { return () => window.removeEventListener("resize", handleResize); }, []); - const isLoading = useSelector((state) => state.instances.isLoading); const loadingInstances = useSelector( (state) => state.instances.loadingInstances ); @@ -123,6 +123,12 @@ const SubHeader = ({ setBottomNav, bottomNav }) => { color: whiteColor, }, }; + const instanceLoading = useSelector((state) => state.instances.isLoading); + + useEffect(() => { + setIsLoading(instanceLoading) + }, [instanceLoading]) + useEffect(() => { // For bulk loading, check if all instances are loaded using bulk count From 4429ad185fd9ade09a23d04e6152ed2160c62f1e Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 23 Apr 2026 18:05:46 -0700 Subject: [PATCH 2/2] #VFB-241 - feat: enhance misalignment handling and loading state management in TerminfoSlider component --- .../frontend/src/components/TermInfo/TerminfoSlider.jsx | 8 +++++--- .../frontend/src/reducers/GlobalReducer.js | 2 +- .../src/reducers/middleware/urlUpdaterMiddleware.js | 2 +- .../frontend/src/shared/subHeader/index.jsx | 8 +------- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/applications/virtual-fly-brain/frontend/src/components/TermInfo/TerminfoSlider.jsx b/applications/virtual-fly-brain/frontend/src/components/TermInfo/TerminfoSlider.jsx index d23debee..41e1d2f0 100644 --- a/applications/virtual-fly-brain/frontend/src/components/TermInfo/TerminfoSlider.jsx +++ b/applications/virtual-fly-brain/frontend/src/components/TermInfo/TerminfoSlider.jsx @@ -165,14 +165,16 @@ const TerminfoSlider = (props) => { useEffect(() => { - if ( misalignedTemplate && !alignedTemplates ){ + const misalignedIdKeys = Object.keys(misalignedIDs || {}); + const activeMisalignedId = misalignedIdKeys[misalignedIdKeys.length - 1]; + if (misalignedTemplate && !alignedTemplates && activeMisalignedId) { setConfirmationModal({ open: true, - example: { id : Object.keys(misalignedIDs) , template: misalignedTemplate }, + example: { id: activeMisalignedId, template: misalignedTemplate }, message: `The image you requested is aligned to another template. Click Load Template to open it in a new tab or Cancel to just view the image metadata.` }); } - }, [misalignedTemplate, misalignedIDs]) + }, [misalignedTemplate, misalignedIDs, alignedTemplates]) useEffect( () => { diff --git a/applications/virtual-fly-brain/frontend/src/reducers/GlobalReducer.js b/applications/virtual-fly-brain/frontend/src/reducers/GlobalReducer.js index 29489010..e8889bd5 100644 --- a/applications/virtual-fly-brain/frontend/src/reducers/GlobalReducer.js +++ b/applications/virtual-fly-brain/frontend/src/reducers/GlobalReducer.js @@ -41,7 +41,7 @@ const GlobalReducer = (state = initialStateGlobalReducer, response) => { return Object.assign({}, state, { alignedTemplates: aligned, misalignedTemplate : templateID, - misalignedIDs : {...state.misalignedIDs, [id] : id } + misalignedIDs : id ? {...state.misalignedIDs, [id] : id } : state.misalignedIDs }) } case getGlobalTypes.OPEN_QUERY_COMPONENT: diff --git a/applications/virtual-fly-brain/frontend/src/reducers/middleware/urlUpdaterMiddleware.js b/applications/virtual-fly-brain/frontend/src/reducers/middleware/urlUpdaterMiddleware.js index 6c43bf1c..a2677272 100644 --- a/applications/virtual-fly-brain/frontend/src/reducers/middleware/urlUpdaterMiddleware.js +++ b/applications/virtual-fly-brain/frontend/src/reducers/middleware/urlUpdaterMiddleware.js @@ -224,7 +224,7 @@ export const urlUpdaterMiddleware = store => next => (action) => { return; } else if (IsTemplate && launchTemplate?.metadata?.Id !== action.payload.Id) { // If it's a template and the launchTemplate is defined, we need to show the widget to open this template in a new tab - store.dispatch(setAlignTemplates(false, action.payload.Id)); + store.dispatch(setAlignTemplates(false, action.payload.Id, action.payload.Id)); return; } diff --git a/applications/virtual-fly-brain/frontend/src/shared/subHeader/index.jsx b/applications/virtual-fly-brain/frontend/src/shared/subHeader/index.jsx index b619e7d5..6780c02a 100644 --- a/applications/virtual-fly-brain/frontend/src/shared/subHeader/index.jsx +++ b/applications/virtual-fly-brain/frontend/src/shared/subHeader/index.jsx @@ -79,7 +79,6 @@ const SubHeader = ({ setBottomNav, bottomNav }) => { const [filterOpened, setFilterOpened] = useState(false); const [selectedFilters, setSelectedFilters] = useState({}); const [desktop, setDesktop] = useState(window.innerWidth >= 1200); - const [isLoading, setIsLoading] = useState(false); useEffect(() => { const handleResize = () => { @@ -90,6 +89,7 @@ const SubHeader = ({ setBottomNav, bottomNav }) => { return () => window.removeEventListener("resize", handleResize); }, []); + const isLoading = useSelector((state) => state.instances.isLoading); const loadingInstances = useSelector( (state) => state.instances.loadingInstances ); @@ -123,12 +123,6 @@ const SubHeader = ({ setBottomNav, bottomNav }) => { color: whiteColor, }, }; - const instanceLoading = useSelector((state) => state.instances.isLoading); - - useEffect(() => { - setIsLoading(instanceLoading) - }, [instanceLoading]) - useEffect(() => { // For bulk loading, check if all instances are loaded using bulk count