From 23d2ee1c2235d601bae4aee048b426d4310b8ff6 Mon Sep 17 00:00:00 2001 From: 8144225309 Date: Wed, 27 May 2026 23:24:35 -0400 Subject: [PATCH] fix(factories): refetch on profile switch + add activeProfileId to route deps Switching the wallet's active node via NodePicker left the factories list empty when the user wasn't on /factories at switch time and then navigated there afterward. The route-effect in routerReduxSync also did not re-fire when activeProfileId changed (deps were only pathname / authStatus / navigate), so even a fresh navigate didn't always recover. Two defensive fixes: 1. NodePicker.handleSwitchNode now eagerly refetches ALL section data (CLN, BKPR, factories) on profile switch instead of only the section matching the current pathname. The fetches are async / fire-and-forget so switch UX is unaffected, but the next page the user navigates to has fresh data for the new profile. 2. routerReduxSync's route-fetch useEffect now includes activeProfileId in its deps. When the active node changes without a pathname change, the effect re-fires for the current pathname and refetches against the new node. Repro before this PR: switch the wallet dropdown from ss-demo-lsp to ss-demo-client (CLI factory-list on the client returns 3 factories) -- the wallet's /factories page renders 0. After this PR: the list re-populates on switch and on subsequent /factories navigation. Also removes the now-unused useLocation import in NodePicker (the pathname-conditional bgRefresh is gone). --- .../components/ui/NodePicker/NodePicker.tsx | 19 +++++++++---------- apps/frontend/src/routes/routerReduxSync.tsx | 9 ++++++++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/apps/frontend/src/components/ui/NodePicker/NodePicker.tsx b/apps/frontend/src/components/ui/NodePicker/NodePicker.tsx index bc4d16bc..9ed36bc3 100644 --- a/apps/frontend/src/components/ui/NodePicker/NodePicker.tsx +++ b/apps/frontend/src/components/ui/NodePicker/NodePicker.tsx @@ -1,7 +1,6 @@ import './NodePicker.scss'; import { Dropdown, Spinner, OverlayTrigger, Tooltip } from 'react-bootstrap'; import { useSelector } from 'react-redux'; -import { useLocation } from 'react-router-dom'; import { useInjectReducer } from '../../../hooks/use-injectreducer'; import nodesReducer from '../../../store/nodesSlice'; import { setIsSwitching, setIsDiscovering, setActiveProfileId } from '../../../store/nodesSlice'; @@ -18,7 +17,6 @@ import logger from '../../../services/logger.service'; const NodePicker = () => { useInjectReducer('nodes', nodesReducer); - const { pathname } = useLocation(); const profiles = useSelector(selectNodeProfiles); const activeProfile = useSelector(selectActiveProfile); @@ -58,16 +56,17 @@ const NodePicker = () => { appStore.dispatch(setIsSwitching(false)); } - // Non-critical background refresh — transactions, offers, plugin detection. + // Non-critical background refresh after profile switch. Eagerly refetches + // ALL section data (factories, CLN dashboard, bookkeeper) regardless of + // current pathname so the next page the user navigates to has fresh data + // for the new profile. Previously only the section matching the current + // pathname was refetched, which left e.g. the factories list empty when + // a user switched profile on /bookkeeper and then navigated to /factories. // Fire-and-forget so a slow query on any node never blocks the UI. - const bgRefresh = pathname.includes('/bookkeeper') - ? BookkeeperService.fetchBKPRData() - : pathname.includes('/factories') - ? FactoriesService.fetchFactoriesData() - : CLNService.fetchCLNData(); - Promise.all([ - bgRefresh, + CLNService.fetchCLNData(), + BookkeeperService.fetchBKPRData(), + FactoriesService.fetchFactoriesData(), NodesService.fetchAndDispatchNodes(), NodesService.detectFactoryPlugin(), ]).catch(err => logger.error('Background post-switch refresh failed:', err)); diff --git a/apps/frontend/src/routes/routerReduxSync.tsx b/apps/frontend/src/routes/routerReduxSync.tsx index 0a985a96..03ba8614 100644 --- a/apps/frontend/src/routes/routerReduxSync.tsx +++ b/apps/frontend/src/routes/routerReduxSync.tsx @@ -8,6 +8,7 @@ import { APP_WAIT_TIME } from '../utilities/constants'; import { useDispatch, useSelector } from 'react-redux'; import { BookkeeperService, CLNService, FactoriesService, NodesService, RootService } from '../services/http.service'; import { selectAuthStatus, selectNodeInfo } from '../store/rootSelectors'; +import { selectActiveProfileId } from '../store/nodesSelectors'; import { appStore } from '../store/appStore'; import logger from '../services/logger.service'; @@ -17,6 +18,7 @@ export function RootRouterReduxSync() { const { pathname } = useLocation(); const authStatus = useSelector(selectAuthStatus); const nodeInfo = useSelector(selectNodeInfo); + const activeProfileId = useSelector(selectActiveProfileId); // Fetch node profiles, run background discovery, detect factory plugin useEffect(() => { @@ -115,7 +117,12 @@ export function RootRouterReduxSync() { if (pathname !== targetPath) { navigate(targetPath, { replace: true }); } - }, [authStatus, pathname, navigate]); + // activeProfileId is in the deps so that switching the wallet's active + // node via NodePicker re-fires the current route's data fetch against + // the new node, even when pathname doesn't change. Without this, navigating + // /bookkeeper -> switch profile -> /factories left the factories list + // empty because the route-effect closure captured the pre-switch state. + }, [authStatus, pathname, navigate, activeProfileId]); // Clear store on route unmounting useEffect(() => {