Minimized API request between Tab view change

This commit is contained in:
yangdx
2025-03-13 19:50:37 +08:00
parent 6893e3c4e2
commit e30162e50a
10 changed files with 304 additions and 105 deletions

View File

@@ -75,8 +75,8 @@ class LightragPathFilter(logging.Filter):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
# Define paths to be filtered # Define paths to be filtered
# self.filtered_paths = ["/documents", "/health", "/webui/"] self.filtered_paths = ["/documents", "/health", "/webui/"]
self.filtered_paths = ["/health", "/webui/"] # self.filtered_paths = ["/health", "/webui/"]
def filter(self, record): def filter(self, record):
try: try:

View File

@@ -22,7 +22,7 @@ import { Tabs, TabsContent } from '@/components/ui/Tabs'
function App() { function App() {
const message = useBackendState.use.message() const message = useBackendState.use.message()
const enableHealthCheck = useSettingsStore.use.enableHealthCheck() const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
const [currentTab] = useState(() => useSettingsStore.getState().currentTab) const currentTab = useSettingsStore.use.currentTab()
const [apiKeyInvalid, setApiKeyInvalid] = useState(false) const [apiKeyInvalid, setApiKeyInvalid] = useState(false)
// Health check // Health check

View File

@@ -1,4 +1,4 @@
import { useCallback } from 'react' import { useCallback, useEffect, useRef } from 'react'
import { AsyncSelect } from '@/components/ui/AsyncSelect' import { AsyncSelect } from '@/components/ui/AsyncSelect'
import { useSettingsStore } from '@/stores/settings' import { useSettingsStore } from '@/stores/settings'
import { useGraphStore } from '@/stores/graph' import { useGraphStore } from '@/stores/graph'
@@ -10,6 +10,37 @@ const GraphLabels = () => {
const { t } = useTranslation() const { t } = useTranslation()
const label = useSettingsStore.use.queryLabel() const label = useSettingsStore.use.queryLabel()
const allDatabaseLabels = useGraphStore.use.allDatabaseLabels() const allDatabaseLabels = useGraphStore.use.allDatabaseLabels()
const labelsLoadedRef = useRef(false)
// Track if a fetch is in progress to prevent multiple simultaneous fetches
const fetchInProgressRef = useRef(false)
// Fetch labels once on component mount, using global flag to prevent duplicates
useEffect(() => {
// Check if we've already attempted to fetch labels in this session
const labelsFetchAttempted = useGraphStore.getState().labelsFetchAttempted
// Only fetch if we haven't attempted in this session and no fetch is in progress
if (!labelsFetchAttempted && !fetchInProgressRef.current) {
fetchInProgressRef.current = true
// Set global flag to indicate we've attempted to fetch in this session
useGraphStore.getState().setLabelsFetchAttempted(true)
console.log('Fetching graph labels (once per session)...')
useGraphStore.getState().fetchAllDatabaseLabels()
.then(() => {
labelsLoadedRef.current = true
fetchInProgressRef.current = false
})
.catch((error) => {
console.error('Failed to fetch labels:', error)
fetchInProgressRef.current = false
// Reset global flag to allow retry
useGraphStore.getState().setLabelsFetchAttempted(false)
})
}
}, []) // Empty dependency array ensures this only runs once on mount
const getSearchEngine = useCallback(() => { const getSearchEngine = useCallback(() => {
// Create search engine // Create search engine

View File

@@ -25,12 +25,10 @@ const TabContent: React.FC<TabContentProps> = ({ tabId, children, className = ''
}; };
}, [tabId, setTabVisibility]); }, [tabId, setTabVisibility]);
if (!isVisible) { // Use CSS to hide content instead of not rendering it
return null; // This prevents components from unmounting when tabs are switched
}
return ( return (
<div className={className}> <div className={`${className} ${isVisible ? '' : 'hidden'}`}>
{children} {children}
</div> </div>
); );

View File

@@ -1,6 +1,7 @@
import React, { useState, useMemo } from 'react'; import React, { useState, useEffect, useMemo } from 'react';
import { TabVisibilityContext } from './context'; import { TabVisibilityContext } from './context';
import { TabVisibilityContextType } from './types'; import { TabVisibilityContextType } from './types';
import { useSettingsStore } from '@/stores/settings';
interface TabVisibilityProviderProps { interface TabVisibilityProviderProps {
children: React.ReactNode; children: React.ReactNode;
@@ -11,7 +12,21 @@ interface TabVisibilityProviderProps {
* Manages the visibility state of tabs throughout the application * Manages the visibility state of tabs throughout the application
*/ */
export const TabVisibilityProvider: React.FC<TabVisibilityProviderProps> = ({ children }) => { export const TabVisibilityProvider: React.FC<TabVisibilityProviderProps> = ({ children }) => {
const [visibleTabs, setVisibleTabs] = useState<Record<string, boolean>>({}); // Get current tab from settings store
const currentTab = useSettingsStore.use.currentTab();
// Initialize visibility state with current tab as visible
const [visibleTabs, setVisibleTabs] = useState<Record<string, boolean>>(() => ({
[currentTab]: true
}));
// Update visibility when current tab changes
useEffect(() => {
setVisibleTabs((prev) => ({
...prev,
[currentTab]: true
}));
}, [currentTab]);
// Create the context value with memoization to prevent unnecessary re-renders // Create the context value with memoization to prevent unnecessary re-renders
const contextValue = useMemo<TabVisibilityContextType>( const contextValue = useMemo<TabVisibilityContextType>(

View File

@@ -1,5 +1,38 @@
import { useState, useEffect } from 'react'
import { useTabVisibility } from '@/contexts/useTabVisibility'
import { backendBaseUrl } from '@/lib/constants' import { backendBaseUrl } from '@/lib/constants'
export default function ApiSite() { export default function ApiSite() {
return <iframe src={backendBaseUrl + '/docs'} className="size-full" /> const { isTabVisible } = useTabVisibility()
const isApiTabVisible = isTabVisible('api')
const [iframeLoaded, setIframeLoaded] = useState(false)
// Load the iframe once on component mount
useEffect(() => {
if (!iframeLoaded) {
setIframeLoaded(true)
}
}, [iframeLoaded])
// Use CSS to hide content when tab is not visible
return (
<div className={`size-full ${isApiTabVisible ? '' : 'hidden'}`}>
{iframeLoaded ? (
<iframe
src={backendBaseUrl + '/docs'}
className="size-full w-full h-full"
style={{ width: '100%', height: '100%', border: 'none' }}
// Use key to ensure iframe doesn't reload
key="api-docs-iframe"
/>
) : (
<div className="flex h-full w-full items-center justify-center bg-background">
<div className="text-center">
<div className="mb-2 h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent"></div>
<p>Loading API Documentation...</p>
</div>
</div>
)}
</div>
)
} }

View File

@@ -1,5 +1,6 @@
import { useState, useEffect, useCallback } from 'react' import { useState, useEffect, useCallback, useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useTabVisibility } from '@/contexts/useTabVisibility'
import Button from '@/components/ui/Button' import Button from '@/components/ui/Button'
import { import {
Table, Table,
@@ -26,6 +27,9 @@ export default function DocumentManager() {
const { t } = useTranslation() const { t } = useTranslation()
const health = useBackendState.use.health() const health = useBackendState.use.health()
const [docs, setDocs] = useState<DocsStatusesResponse | null>(null) const [docs, setDocs] = useState<DocsStatusesResponse | null>(null)
const { isTabVisible } = useTabVisibility()
const isDocumentsTabVisible = isTabVisible('documents')
const initialLoadRef = useRef(false)
const fetchDocuments = useCallback(async () => { const fetchDocuments = useCallback(async () => {
try { try {
@@ -50,9 +54,13 @@ export default function DocumentManager() {
} }
}, [setDocs, t]) }, [setDocs, t])
// Only fetch documents when the tab becomes visible for the first time
useEffect(() => { useEffect(() => {
fetchDocuments() if (isDocumentsTabVisible && !initialLoadRef.current) {
}, [fetchDocuments, t]) fetchDocuments()
initialLoadRef.current = true
}
}, [isDocumentsTabVisible, fetchDocuments])
const scanDocuments = useCallback(async () => { const scanDocuments = useCallback(async () => {
try { try {
@@ -63,19 +71,22 @@ export default function DocumentManager() {
} }
}, [t]) }, [t])
// Only set up polling when the tab is visible and health is good
useEffect(() => { useEffect(() => {
if (!isDocumentsTabVisible || !health) {
return
}
const interval = setInterval(async () => { const interval = setInterval(async () => {
if (!health) {
return
}
try { try {
await fetchDocuments() await fetchDocuments()
} catch (err) { } catch (err) {
toast.error(t('documentPanel.documentManager.errors.scanProgressFailed', { error: errorMessage(err) })) toast.error(t('documentPanel.documentManager.errors.scanProgressFailed', { error: errorMessage(err) }))
} }
}, 5000) }, 5000)
return () => clearInterval(interval) return () => clearInterval(interval)
}, [health, fetchDocuments, t]) }, [health, fetchDocuments, t, isDocumentsTabVisible])
return ( return (
<Card className="!size-full !rounded-none !border-none"> <Card className="!size-full !rounded-none !border-none">

View File

@@ -1,4 +1,5 @@
import { useEffect, useState, useCallback, useMemo, useRef } from 'react' import { useEffect, useState, useCallback, useMemo, useRef } from 'react'
import { useTabVisibility } from '@/contexts/useTabVisibility'
// import { MiniMap } from '@react-sigma/minimap' // import { MiniMap } from '@react-sigma/minimap'
import { SigmaContainer, useRegisterEvents, useSigma } from '@react-sigma/core' import { SigmaContainer, useRegisterEvents, useSigma } from '@react-sigma/core'
import { Settings as SigmaSettings } from 'sigma/settings' import { Settings as SigmaSettings } from 'sigma/settings'
@@ -107,10 +108,17 @@ const GraphEvents = () => {
const GraphViewer = () => { const GraphViewer = () => {
const [sigmaSettings, setSigmaSettings] = useState(defaultSigmaSettings) const [sigmaSettings, setSigmaSettings] = useState(defaultSigmaSettings)
const sigmaRef = useRef<any>(null) const sigmaRef = useRef<any>(null)
const initAttemptedRef = useRef(false)
const selectedNode = useGraphStore.use.selectedNode() const selectedNode = useGraphStore.use.selectedNode()
const focusedNode = useGraphStore.use.focusedNode() const focusedNode = useGraphStore.use.focusedNode()
const moveToSelectedNode = useGraphStore.use.moveToSelectedNode() const moveToSelectedNode = useGraphStore.use.moveToSelectedNode()
const isFetching = useGraphStore.use.isFetching()
const shouldRender = useGraphStore.use.shouldRender() // Rendering control state
// Get tab visibility
const { isTabVisible } = useTabVisibility()
const isGraphTabVisible = isTabVisible('knowledge-graph')
const showPropertyPanel = useSettingsStore.use.showPropertyPanel() const showPropertyPanel = useSettingsStore.use.showPropertyPanel()
const showNodeSearchBar = useSettingsStore.use.showNodeSearchBar() const showNodeSearchBar = useSettingsStore.use.showNodeSearchBar()
@@ -120,6 +128,15 @@ const GraphViewer = () => {
const enableNodeDrag = useSettingsStore.use.enableNodeDrag() const enableNodeDrag = useSettingsStore.use.enableNodeDrag()
const renderEdgeLabels = useSettingsStore.use.showEdgeLabel() const renderEdgeLabels = useSettingsStore.use.showEdgeLabel()
// Ensure rendering is enabled when tab becomes visible
useEffect(() => {
if (isGraphTabVisible && !shouldRender && !isFetching && !initAttemptedRef.current) {
// If tab is visible but graph is not rendering, try to enable rendering
useGraphStore.getState().setShouldRender(true)
initAttemptedRef.current = true
}
}, [isGraphTabVisible, shouldRender, isFetching])
useEffect(() => { useEffect(() => {
setSigmaSettings({ setSigmaSettings({
...defaultSigmaSettings, ...defaultSigmaSettings,
@@ -148,6 +165,24 @@ const GraphViewer = () => {
[selectedNode] [selectedNode]
) )
// If we shouldn't render, show loading state or empty state
if (!shouldRender) {
return (
<div className="flex h-full w-full items-center justify-center bg-background">
{isFetching ? (
<div className="text-center">
<div className="mb-2 h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent"></div>
<p>Reloading Graph Data...</p>
</div>
) : (
<div className="text-center text-muted-foreground">
{/* Empty or hint message */}
</div>
)}
</div>
)
}
return ( return (
<SigmaContainer <SigmaContainer
settings={sigmaSettings} settings={sigmaSettings}

View File

@@ -6,6 +6,7 @@ import { useGraphStore, RawGraph } from '@/stores/graph'
import { queryGraphs } from '@/api/lightrag' import { queryGraphs } from '@/api/lightrag'
import { useBackendState } from '@/stores/state' import { useBackendState } from '@/stores/state'
import { useSettingsStore } from '@/stores/settings' import { useSettingsStore } from '@/stores/settings'
import { useTabVisibility } from '@/contexts/useTabVisibility'
import seedrandom from 'seedrandom' import seedrandom from 'seedrandom'
@@ -169,25 +170,22 @@ const useLightrangeGraph = () => {
const minDegree = useSettingsStore.use.graphMinDegree() const minDegree = useSettingsStore.use.graphMinDegree()
const isFetching = useGraphStore.use.isFetching() const isFetching = useGraphStore.use.isFetching()
// Use ref to track fetch status // Get tab visibility
const fetchStatusRef = useRef<Record<string, boolean>>({}); const { isTabVisible } = useTabVisibility()
const isGraphTabVisible = isTabVisible('knowledge-graph')
// Track previous parameters to detect actual changes // Track previous parameters to detect actual changes
const prevParamsRef = useRef({ queryLabel, maxQueryDepth, minDegree }); const prevParamsRef = useRef({ queryLabel, maxQueryDepth, minDegree })
// Reset fetch status only when parameters actually change // Use ref to track if data has been loaded and initial load
useEffect(() => { const dataLoadedRef = useRef(false)
const prevParams = prevParamsRef.current; const initialLoadRef = useRef(false)
if (prevParams.queryLabel !== queryLabel ||
prevParams.maxQueryDepth !== maxQueryDepth || // Check if parameters have changed
prevParams.minDegree !== minDegree) { const paramsChanged =
useGraphStore.getState().setIsFetching(false); prevParamsRef.current.queryLabel !== queryLabel ||
// Reset fetch status for new parameters prevParamsRef.current.maxQueryDepth !== maxQueryDepth ||
fetchStatusRef.current = {}; prevParamsRef.current.minDegree !== minDegree
// Update previous parameters
prevParamsRef.current = { queryLabel, maxQueryDepth, minDegree };
}
}, [queryLabel, maxQueryDepth, minDegree, isFetching])
const getNode = useCallback( const getNode = useCallback(
(nodeId: string) => { (nodeId: string) => {
@@ -203,77 +201,131 @@ const useLightrangeGraph = () => {
[rawGraph] [rawGraph]
) )
// Track if a fetch is in progress to prevent multiple simultaneous fetches
const fetchInProgressRef = useRef(false)
// Data fetching logic - use a separate effect with minimal dependencies to prevent multiple triggers
useEffect(() => { useEffect(() => {
if (queryLabel) { // Skip if fetch is already in progress
const fetchKey = `${queryLabel}-${maxQueryDepth}-${minDegree}`; if (fetchInProgressRef.current) {
return
}
// Only fetch if we haven't fetched this combination in the current component lifecycle // If there's no query label, reset the graph only if it hasn't been reset already
if (!isFetching && !fetchStatusRef.current[fetchKey]) { if (!queryLabel) {
const state = useGraphStore.getState(); if (rawGraph !== null || sigmaGraph !== null) {
// Clear selection and highlighted nodes before fetching new graph const state = useGraphStore.getState()
state.clearSelection(); state.reset()
if (state.sigmaGraph) { state.setSigmaGraph(new DirectedGraph())
state.sigmaGraph.forEachNode((node) => { state.setGraphLabels(['*'])
state.sigmaGraph?.setNodeAttribute(node, 'highlighted', false); // Reset fetch attempt flags when resetting graph
}); state.setGraphDataFetchAttempted(false)
} state.setLabelsFetchAttempted(false)
}
dataLoadedRef.current = false
initialLoadRef.current = false
return
}
state.setIsFetching(true); // Check if we've already attempted to fetch this data in this session
fetchStatusRef.current[fetchKey] = true; const graphDataFetchAttempted = useGraphStore.getState().graphDataFetchAttempted
fetchGraph(queryLabel, maxQueryDepth, minDegree).then((data) => {
const state = useGraphStore.getState()
const newSigmaGraph = createSigmaGraph(data)
data?.buildDynamicMap()
// Update all graph data at once to minimize UI flicker // Fetch data if:
state.clearSelection() // 1. We're not already fetching
state.setMoveToSelectedNode(false) // 2. We haven't attempted to fetch in this session OR parameters have changed
state.setSigmaGraph(newSigmaGraph) if (!isFetching && !fetchInProgressRef.current && (!graphDataFetchAttempted || paramsChanged)) {
state.setRawGraph(data) // Set flag to prevent multiple fetches
fetchInProgressRef.current = true
// Set global flag to indicate we've attempted to fetch in this session
useGraphStore.getState().setGraphDataFetchAttempted(true)
// Extract labels from current graph data const state = useGraphStore.getState()
if (data) {
const labelSet = new Set<string>(); // Set rendering control state
for (const node of data.nodes) { state.setIsFetching(true)
if (node.labels && Array.isArray(node.labels)) { state.setShouldRender(false) // Disable rendering during data loading
for (const label of node.labels) {
if (label !== '*') { // filter out label "*" // Clear selection and highlighted nodes before fetching new graph
labelSet.add(label); state.clearSelection()
} if (state.sigmaGraph) {
state.sigmaGraph.forEachNode((node) => {
state.sigmaGraph?.setNodeAttribute(node, 'highlighted', false)
})
}
// Update parameter reference
prevParamsRef.current = { queryLabel, maxQueryDepth, minDegree }
console.log('Fetching graph data (once per session unless params change)...')
// Use a local copy of the parameters to avoid closure issues
const currentQueryLabel = queryLabel
const currentMaxQueryDepth = maxQueryDepth
const currentMinDegree = minDegree
fetchGraph(currentQueryLabel, currentMaxQueryDepth, currentMinDegree).then((data) => {
const state = useGraphStore.getState()
const newSigmaGraph = createSigmaGraph(data)
data?.buildDynamicMap()
// Update all graph data at once to minimize UI flicker
state.clearSelection()
state.setMoveToSelectedNode(false)
state.setSigmaGraph(newSigmaGraph)
state.setRawGraph(data)
// Extract labels from current graph data for local use
if (data) {
const labelSet = new Set<string>()
for (const node of data.nodes) {
if (node.labels && Array.isArray(node.labels)) {
for (const label of node.labels) {
if (label !== '*') { // filter out label "*"
labelSet.add(label)
} }
} }
} }
// Put * on top of other labels
const sortedLabels = Array.from(labelSet).sort();
state.setGraphLabels(['*', ...sortedLabels]);
} else {
// Ensure * is there eventhough there is no graph data
state.setGraphLabels(['*']);
} }
// Put * on top of other labels
const sortedLabels = Array.from(labelSet).sort()
state.setGraphLabels(['*', ...sortedLabels])
} else {
// Ensure * is there eventhough there is no graph data
state.setGraphLabels(['*'])
}
// Fetch all database labels after graph update // Mark data as loaded and initial load completed
state.fetchAllDatabaseLabels(); dataLoadedRef.current = true
if (!data) { initialLoadRef.current = true
// If data is invalid, remove the fetch flag to allow retry fetchInProgressRef.current = false
delete fetchStatusRef.current[fetchKey];
} // Reset camera view by triggering FocusOnNode component
// Reset fetching state after all updates are complete state.setMoveToSelectedNode(true)
// Reset camera view by triggering FocusOnNode component
state.setMoveToSelectedNode(true); // Enable rendering if the tab is visible
state.setIsFetching(false); state.setShouldRender(isGraphTabVisible)
}).catch(() => { state.setIsFetching(false)
// Reset fetching state and remove flag in case of error }).catch((error) => {
useGraphStore.getState().setIsFetching(false); console.error('Error fetching graph data:', error)
delete fetchStatusRef.current[fetchKey]; // Reset fetching state and remove flag in case of error
}) const state = useGraphStore.getState()
} state.setIsFetching(false)
} else { state.setShouldRender(isGraphTabVisible) // Restore rendering state
const state = useGraphStore.getState() dataLoadedRef.current = false // Allow retry
state.reset() fetchInProgressRef.current = false
state.setSigmaGraph(new DirectedGraph()) // Reset global flag to allow retry
state.setGraphLabels(['*']) state.setGraphDataFetchAttempted(false)
})
} }
}, [queryLabel, maxQueryDepth, minDegree, isFetching]) }, [queryLabel, maxQueryDepth, minDegree, isFetching, paramsChanged, isGraphTabVisible, rawGraph, sigmaGraph]) // Added missing dependencies
// Update rendering state when tab visibility changes
useEffect(() => {
// Only update rendering state if data is loaded and not fetching
if (rawGraph) {
useGraphStore.getState().setShouldRender(isGraphTabVisible)
}
}, [isGraphTabVisible, rawGraph])
const lightrageGraph = useCallback(() => { const lightrageGraph = useCallback(() => {
if (sigmaGraph) { if (sigmaGraph) {

View File

@@ -71,6 +71,11 @@ interface GraphState {
moveToSelectedNode: boolean moveToSelectedNode: boolean
isFetching: boolean isFetching: boolean
shouldRender: boolean
// Global flags to track data fetching attempts
graphDataFetchAttempted: boolean
labelsFetchAttempted: boolean
refreshLayout: () => void refreshLayout: () => void
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => void setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => void
@@ -88,6 +93,11 @@ interface GraphState {
setAllDatabaseLabels: (labels: string[]) => void setAllDatabaseLabels: (labels: string[]) => void
fetchAllDatabaseLabels: () => Promise<void> fetchAllDatabaseLabels: () => Promise<void>
setIsFetching: (isFetching: boolean) => void setIsFetching: (isFetching: boolean) => void
setShouldRender: (shouldRender: boolean) => void
// Methods to set global flags
setGraphDataFetchAttempted: (attempted: boolean) => void
setLabelsFetchAttempted: (attempted: boolean) => void
} }
const useGraphStoreBase = create<GraphState>()((set, get) => ({ const useGraphStoreBase = create<GraphState>()((set, get) => ({
@@ -98,6 +108,11 @@ const useGraphStoreBase = create<GraphState>()((set, get) => ({
moveToSelectedNode: false, moveToSelectedNode: false,
isFetching: false, isFetching: false,
shouldRender: false,
// Initialize global flags
graphDataFetchAttempted: false,
labelsFetchAttempted: false,
rawGraph: null, rawGraph: null,
sigmaGraph: null, sigmaGraph: null,
@@ -116,6 +131,7 @@ const useGraphStoreBase = create<GraphState>()((set, get) => ({
}, },
setIsFetching: (isFetching: boolean) => set({ isFetching }), setIsFetching: (isFetching: boolean) => set({ isFetching }),
setShouldRender: (shouldRender: boolean) => set({ shouldRender }),
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) =>
set({ selectedNode: nodeId, moveToSelectedNode }), set({ selectedNode: nodeId, moveToSelectedNode }),
setFocusedNode: (nodeId: string | null) => set({ focusedNode: nodeId }), setFocusedNode: (nodeId: string | null) => set({ focusedNode: nodeId }),
@@ -137,7 +153,8 @@ const useGraphStoreBase = create<GraphState>()((set, get) => ({
rawGraph: null, rawGraph: null,
sigmaGraph: null, sigmaGraph: null,
graphLabels: ['*'], graphLabels: ['*'],
moveToSelectedNode: false moveToSelectedNode: false,
shouldRender: false
}), }),
setRawGraph: (rawGraph: RawGraph | null) => setRawGraph: (rawGraph: RawGraph | null) =>
@@ -153,15 +170,22 @@ const useGraphStoreBase = create<GraphState>()((set, get) => ({
fetchAllDatabaseLabels: async () => { fetchAllDatabaseLabels: async () => {
try { try {
console.log('Fetching all database labels...');
const labels = await getGraphLabels(); const labels = await getGraphLabels();
set({ allDatabaseLabels: ['*', ...labels] }); set({ allDatabaseLabels: ['*', ...labels] });
return;
} catch (error) { } catch (error) {
console.error('Failed to fetch all database labels:', error); console.error('Failed to fetch all database labels:', error);
set({ allDatabaseLabels: ['*'] }); set({ allDatabaseLabels: ['*'] });
throw error;
} }
}, },
setMoveToSelectedNode: (moveToSelectedNode?: boolean) => set({ moveToSelectedNode }) setMoveToSelectedNode: (moveToSelectedNode?: boolean) => set({ moveToSelectedNode }),
// Methods to set global flags
setGraphDataFetchAttempted: (attempted: boolean) => set({ graphDataFetchAttempted: attempted }),
setLabelsFetchAttempted: (attempted: boolean) => set({ labelsFetchAttempted: attempted })
})) }))
const useGraphStore = createSelectors(useGraphStoreBase) const useGraphStore = createSelectors(useGraphStoreBase)