Minimized API request between Tab view change
This commit is contained in:
@@ -75,8 +75,8 @@ class LightragPathFilter(logging.Filter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# Define paths to be filtered
|
||||
# self.filtered_paths = ["/documents", "/health", "/webui/"]
|
||||
self.filtered_paths = ["/health", "/webui/"]
|
||||
self.filtered_paths = ["/documents", "/health", "/webui/"]
|
||||
# self.filtered_paths = ["/health", "/webui/"]
|
||||
|
||||
def filter(self, record):
|
||||
try:
|
||||
|
@@ -22,7 +22,7 @@ import { Tabs, TabsContent } from '@/components/ui/Tabs'
|
||||
function App() {
|
||||
const message = useBackendState.use.message()
|
||||
const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
|
||||
const [currentTab] = useState(() => useSettingsStore.getState().currentTab)
|
||||
const currentTab = useSettingsStore.use.currentTab()
|
||||
const [apiKeyInvalid, setApiKeyInvalid] = useState(false)
|
||||
|
||||
// Health check
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { AsyncSelect } from '@/components/ui/AsyncSelect'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
import { useGraphStore } from '@/stores/graph'
|
||||
@@ -10,6 +10,37 @@ const GraphLabels = () => {
|
||||
const { t } = useTranslation()
|
||||
const label = useSettingsStore.use.queryLabel()
|
||||
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(() => {
|
||||
// Create search engine
|
||||
|
@@ -25,12 +25,10 @@ const TabContent: React.FC<TabContentProps> = ({ tabId, children, className = ''
|
||||
};
|
||||
}, [tabId, setTabVisibility]);
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use CSS to hide content instead of not rendering it
|
||||
// This prevents components from unmounting when tabs are switched
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className={`${className} ${isVisible ? '' : 'hidden'}`}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { TabVisibilityContext } from './context';
|
||||
import { TabVisibilityContextType } from './types';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
|
||||
interface TabVisibilityProviderProps {
|
||||
children: React.ReactNode;
|
||||
@@ -11,7 +12,21 @@ interface TabVisibilityProviderProps {
|
||||
* Manages the visibility state of tabs throughout the application
|
||||
*/
|
||||
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
|
||||
const contextValue = useMemo<TabVisibilityContextType>(
|
||||
|
@@ -1,5 +1,38 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useTabVisibility } from '@/contexts/useTabVisibility'
|
||||
import { backendBaseUrl } from '@/lib/constants'
|
||||
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useTabVisibility } from '@/contexts/useTabVisibility'
|
||||
import Button from '@/components/ui/Button'
|
||||
import {
|
||||
Table,
|
||||
@@ -26,6 +27,9 @@ export default function DocumentManager() {
|
||||
const { t } = useTranslation()
|
||||
const health = useBackendState.use.health()
|
||||
const [docs, setDocs] = useState<DocsStatusesResponse | null>(null)
|
||||
const { isTabVisible } = useTabVisibility()
|
||||
const isDocumentsTabVisible = isTabVisible('documents')
|
||||
const initialLoadRef = useRef(false)
|
||||
|
||||
const fetchDocuments = useCallback(async () => {
|
||||
try {
|
||||
@@ -50,9 +54,13 @@ export default function DocumentManager() {
|
||||
}
|
||||
}, [setDocs, t])
|
||||
|
||||
// Only fetch documents when the tab becomes visible for the first time
|
||||
useEffect(() => {
|
||||
fetchDocuments()
|
||||
}, [fetchDocuments, t])
|
||||
if (isDocumentsTabVisible && !initialLoadRef.current) {
|
||||
fetchDocuments()
|
||||
initialLoadRef.current = true
|
||||
}
|
||||
}, [isDocumentsTabVisible, fetchDocuments])
|
||||
|
||||
const scanDocuments = useCallback(async () => {
|
||||
try {
|
||||
@@ -63,19 +71,22 @@ export default function DocumentManager() {
|
||||
}
|
||||
}, [t])
|
||||
|
||||
// Only set up polling when the tab is visible and health is good
|
||||
useEffect(() => {
|
||||
if (!isDocumentsTabVisible || !health) {
|
||||
return
|
||||
}
|
||||
|
||||
const interval = setInterval(async () => {
|
||||
if (!health) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await fetchDocuments()
|
||||
} catch (err) {
|
||||
toast.error(t('documentPanel.documentManager.errors.scanProgressFailed', { error: errorMessage(err) }))
|
||||
}
|
||||
}, 5000)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [health, fetchDocuments, t])
|
||||
}, [health, fetchDocuments, t, isDocumentsTabVisible])
|
||||
|
||||
return (
|
||||
<Card className="!size-full !rounded-none !border-none">
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState, useCallback, useMemo, useRef } from 'react'
|
||||
import { useTabVisibility } from '@/contexts/useTabVisibility'
|
||||
// import { MiniMap } from '@react-sigma/minimap'
|
||||
import { SigmaContainer, useRegisterEvents, useSigma } from '@react-sigma/core'
|
||||
import { Settings as SigmaSettings } from 'sigma/settings'
|
||||
@@ -107,10 +108,17 @@ const GraphEvents = () => {
|
||||
const GraphViewer = () => {
|
||||
const [sigmaSettings, setSigmaSettings] = useState(defaultSigmaSettings)
|
||||
const sigmaRef = useRef<any>(null)
|
||||
const initAttemptedRef = useRef(false)
|
||||
|
||||
const selectedNode = useGraphStore.use.selectedNode()
|
||||
const focusedNode = useGraphStore.use.focusedNode()
|
||||
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 showNodeSearchBar = useSettingsStore.use.showNodeSearchBar()
|
||||
@@ -120,6 +128,15 @@ const GraphViewer = () => {
|
||||
const enableNodeDrag = useSettingsStore.use.enableNodeDrag()
|
||||
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(() => {
|
||||
setSigmaSettings({
|
||||
...defaultSigmaSettings,
|
||||
@@ -148,6 +165,24 @@ const GraphViewer = () => {
|
||||
[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 (
|
||||
<SigmaContainer
|
||||
settings={sigmaSettings}
|
||||
|
@@ -6,6 +6,7 @@ import { useGraphStore, RawGraph } from '@/stores/graph'
|
||||
import { queryGraphs } from '@/api/lightrag'
|
||||
import { useBackendState } from '@/stores/state'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
import { useTabVisibility } from '@/contexts/useTabVisibility'
|
||||
|
||||
import seedrandom from 'seedrandom'
|
||||
|
||||
@@ -169,25 +170,22 @@ const useLightrangeGraph = () => {
|
||||
const minDegree = useSettingsStore.use.graphMinDegree()
|
||||
const isFetching = useGraphStore.use.isFetching()
|
||||
|
||||
// Use ref to track fetch status
|
||||
const fetchStatusRef = useRef<Record<string, boolean>>({});
|
||||
// Get tab visibility
|
||||
const { isTabVisible } = useTabVisibility()
|
||||
const isGraphTabVisible = isTabVisible('knowledge-graph')
|
||||
|
||||
// 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
|
||||
useEffect(() => {
|
||||
const prevParams = prevParamsRef.current;
|
||||
if (prevParams.queryLabel !== queryLabel ||
|
||||
prevParams.maxQueryDepth !== maxQueryDepth ||
|
||||
prevParams.minDegree !== minDegree) {
|
||||
useGraphStore.getState().setIsFetching(false);
|
||||
// Reset fetch status for new parameters
|
||||
fetchStatusRef.current = {};
|
||||
// Update previous parameters
|
||||
prevParamsRef.current = { queryLabel, maxQueryDepth, minDegree };
|
||||
}
|
||||
}, [queryLabel, maxQueryDepth, minDegree, isFetching])
|
||||
// Use ref to track if data has been loaded and initial load
|
||||
const dataLoadedRef = useRef(false)
|
||||
const initialLoadRef = useRef(false)
|
||||
|
||||
// Check if parameters have changed
|
||||
const paramsChanged =
|
||||
prevParamsRef.current.queryLabel !== queryLabel ||
|
||||
prevParamsRef.current.maxQueryDepth !== maxQueryDepth ||
|
||||
prevParamsRef.current.minDegree !== minDegree
|
||||
|
||||
const getNode = useCallback(
|
||||
(nodeId: string) => {
|
||||
@@ -203,77 +201,131 @@ const useLightrangeGraph = () => {
|
||||
[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(() => {
|
||||
if (queryLabel) {
|
||||
const fetchKey = `${queryLabel}-${maxQueryDepth}-${minDegree}`;
|
||||
// Skip if fetch is already in progress
|
||||
if (fetchInProgressRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
// Only fetch if we haven't fetched this combination in the current component lifecycle
|
||||
if (!isFetching && !fetchStatusRef.current[fetchKey]) {
|
||||
const state = useGraphStore.getState();
|
||||
// Clear selection and highlighted nodes before fetching new graph
|
||||
state.clearSelection();
|
||||
if (state.sigmaGraph) {
|
||||
state.sigmaGraph.forEachNode((node) => {
|
||||
state.sigmaGraph?.setNodeAttribute(node, 'highlighted', false);
|
||||
});
|
||||
}
|
||||
// If there's no query label, reset the graph only if it hasn't been reset already
|
||||
if (!queryLabel) {
|
||||
if (rawGraph !== null || sigmaGraph !== null) {
|
||||
const state = useGraphStore.getState()
|
||||
state.reset()
|
||||
state.setSigmaGraph(new DirectedGraph())
|
||||
state.setGraphLabels(['*'])
|
||||
// Reset fetch attempt flags when resetting graph
|
||||
state.setGraphDataFetchAttempted(false)
|
||||
state.setLabelsFetchAttempted(false)
|
||||
}
|
||||
dataLoadedRef.current = false
|
||||
initialLoadRef.current = false
|
||||
return
|
||||
}
|
||||
|
||||
state.setIsFetching(true);
|
||||
fetchStatusRef.current[fetchKey] = true;
|
||||
fetchGraph(queryLabel, maxQueryDepth, minDegree).then((data) => {
|
||||
const state = useGraphStore.getState()
|
||||
const newSigmaGraph = createSigmaGraph(data)
|
||||
data?.buildDynamicMap()
|
||||
// Check if we've already attempted to fetch this data in this session
|
||||
const graphDataFetchAttempted = useGraphStore.getState().graphDataFetchAttempted
|
||||
|
||||
// Update all graph data at once to minimize UI flicker
|
||||
state.clearSelection()
|
||||
state.setMoveToSelectedNode(false)
|
||||
state.setSigmaGraph(newSigmaGraph)
|
||||
state.setRawGraph(data)
|
||||
// Fetch data if:
|
||||
// 1. We're not already fetching
|
||||
// 2. We haven't attempted to fetch in this session OR parameters have changed
|
||||
if (!isFetching && !fetchInProgressRef.current && (!graphDataFetchAttempted || paramsChanged)) {
|
||||
// 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
|
||||
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);
|
||||
}
|
||||
const state = useGraphStore.getState()
|
||||
|
||||
// Set rendering control state
|
||||
state.setIsFetching(true)
|
||||
state.setShouldRender(false) // Disable rendering during data loading
|
||||
|
||||
// Clear selection and highlighted nodes before fetching new graph
|
||||
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
|
||||
state.fetchAllDatabaseLabels();
|
||||
if (!data) {
|
||||
// If data is invalid, remove the fetch flag to allow retry
|
||||
delete fetchStatusRef.current[fetchKey];
|
||||
}
|
||||
// Reset fetching state after all updates are complete
|
||||
// Reset camera view by triggering FocusOnNode component
|
||||
state.setMoveToSelectedNode(true);
|
||||
state.setIsFetching(false);
|
||||
}).catch(() => {
|
||||
// Reset fetching state and remove flag in case of error
|
||||
useGraphStore.getState().setIsFetching(false);
|
||||
delete fetchStatusRef.current[fetchKey];
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const state = useGraphStore.getState()
|
||||
state.reset()
|
||||
state.setSigmaGraph(new DirectedGraph())
|
||||
state.setGraphLabels(['*'])
|
||||
// Mark data as loaded and initial load completed
|
||||
dataLoadedRef.current = true
|
||||
initialLoadRef.current = true
|
||||
fetchInProgressRef.current = false
|
||||
|
||||
// Reset camera view by triggering FocusOnNode component
|
||||
state.setMoveToSelectedNode(true)
|
||||
|
||||
// Enable rendering if the tab is visible
|
||||
state.setShouldRender(isGraphTabVisible)
|
||||
state.setIsFetching(false)
|
||||
}).catch((error) => {
|
||||
console.error('Error fetching graph data:', error)
|
||||
// Reset fetching state and remove flag in case of error
|
||||
const state = useGraphStore.getState()
|
||||
state.setIsFetching(false)
|
||||
state.setShouldRender(isGraphTabVisible) // Restore rendering state
|
||||
dataLoadedRef.current = false // Allow retry
|
||||
fetchInProgressRef.current = false
|
||||
// Reset global flag to allow retry
|
||||
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(() => {
|
||||
if (sigmaGraph) {
|
||||
|
@@ -71,6 +71,11 @@ interface GraphState {
|
||||
|
||||
moveToSelectedNode: boolean
|
||||
isFetching: boolean
|
||||
shouldRender: boolean
|
||||
|
||||
// Global flags to track data fetching attempts
|
||||
graphDataFetchAttempted: boolean
|
||||
labelsFetchAttempted: boolean
|
||||
|
||||
refreshLayout: () => void
|
||||
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => void
|
||||
@@ -88,6 +93,11 @@ interface GraphState {
|
||||
setAllDatabaseLabels: (labels: string[]) => void
|
||||
fetchAllDatabaseLabels: () => Promise<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) => ({
|
||||
@@ -98,6 +108,11 @@ const useGraphStoreBase = create<GraphState>()((set, get) => ({
|
||||
|
||||
moveToSelectedNode: false,
|
||||
isFetching: false,
|
||||
shouldRender: false,
|
||||
|
||||
// Initialize global flags
|
||||
graphDataFetchAttempted: false,
|
||||
labelsFetchAttempted: false,
|
||||
|
||||
rawGraph: null,
|
||||
sigmaGraph: null,
|
||||
@@ -116,6 +131,7 @@ const useGraphStoreBase = create<GraphState>()((set, get) => ({
|
||||
},
|
||||
|
||||
setIsFetching: (isFetching: boolean) => set({ isFetching }),
|
||||
setShouldRender: (shouldRender: boolean) => set({ shouldRender }),
|
||||
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) =>
|
||||
set({ selectedNode: nodeId, moveToSelectedNode }),
|
||||
setFocusedNode: (nodeId: string | null) => set({ focusedNode: nodeId }),
|
||||
@@ -137,7 +153,8 @@ const useGraphStoreBase = create<GraphState>()((set, get) => ({
|
||||
rawGraph: null,
|
||||
sigmaGraph: null,
|
||||
graphLabels: ['*'],
|
||||
moveToSelectedNode: false
|
||||
moveToSelectedNode: false,
|
||||
shouldRender: false
|
||||
}),
|
||||
|
||||
setRawGraph: (rawGraph: RawGraph | null) =>
|
||||
@@ -153,15 +170,22 @@ const useGraphStoreBase = create<GraphState>()((set, get) => ({
|
||||
|
||||
fetchAllDatabaseLabels: async () => {
|
||||
try {
|
||||
console.log('Fetching all database labels...');
|
||||
const labels = await getGraphLabels();
|
||||
set({ allDatabaseLabels: ['*', ...labels] });
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch all database labels:', error);
|
||||
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)
|
||||
|
Reference in New Issue
Block a user