Merge pull request #2291 from danielaskdd/reload-popular-labels
Refact: Auto-refresh of Popular Labels When Pipeline Completes
This commit is contained in:
commit
08b0283b04
2 changed files with 82 additions and 4 deletions
|
|
@ -1,7 +1,8 @@
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState, 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'
|
||||||
|
import { useBackendState } from '@/stores/state'
|
||||||
import {
|
import {
|
||||||
dropdownDisplayLimit,
|
dropdownDisplayLimit,
|
||||||
controlButtonVariant,
|
controlButtonVariant,
|
||||||
|
|
@ -22,6 +23,11 @@ const GraphLabels = () => {
|
||||||
const [refreshTrigger, setRefreshTrigger] = useState(0)
|
const [refreshTrigger, setRefreshTrigger] = useState(0)
|
||||||
const [selectKey, setSelectKey] = useState(0)
|
const [selectKey, setSelectKey] = useState(0)
|
||||||
|
|
||||||
|
// Pipeline state monitoring
|
||||||
|
const pipelineBusy = useBackendState.use.pipelineBusy()
|
||||||
|
const prevPipelineBusy = useRef<boolean | undefined>(undefined)
|
||||||
|
const shouldRefreshPopularLabelsRef = useRef(false)
|
||||||
|
|
||||||
// Dynamic tooltip based on current label state
|
// Dynamic tooltip based on current label state
|
||||||
const getRefreshTooltip = useCallback(() => {
|
const getRefreshTooltip = useCallback(() => {
|
||||||
if (isRefreshing) {
|
if (isRefreshing) {
|
||||||
|
|
@ -67,6 +73,49 @@ const GraphLabels = () => {
|
||||||
}
|
}
|
||||||
}, [dropdownRefreshTrigger])
|
}, [dropdownRefreshTrigger])
|
||||||
|
|
||||||
|
// Monitor pipeline state changes: busy -> idle
|
||||||
|
useEffect(() => {
|
||||||
|
if (prevPipelineBusy.current === true && pipelineBusy === false) {
|
||||||
|
console.log('Pipeline changed from busy to idle, marking for popular labels refresh')
|
||||||
|
shouldRefreshPopularLabelsRef.current = true
|
||||||
|
}
|
||||||
|
prevPipelineBusy.current = pipelineBusy
|
||||||
|
}, [pipelineBusy])
|
||||||
|
|
||||||
|
// Helper: Reload popular labels from backend
|
||||||
|
const reloadPopularLabels = useCallback(async () => {
|
||||||
|
if (!shouldRefreshPopularLabelsRef.current) return
|
||||||
|
|
||||||
|
console.log('Reloading popular labels (triggered by pipeline idle)')
|
||||||
|
try {
|
||||||
|
const popularLabels = await getPopularLabels(popularLabelsDefaultLimit)
|
||||||
|
SearchHistoryManager.clearHistory()
|
||||||
|
|
||||||
|
if (popularLabels.length === 0) {
|
||||||
|
const fallbackLabels = ['entity', 'relationship', 'document', 'concept']
|
||||||
|
await SearchHistoryManager.initializeWithDefaults(fallbackLabels)
|
||||||
|
} else {
|
||||||
|
await SearchHistoryManager.initializeWithDefaults(popularLabels)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to reload popular labels:', error)
|
||||||
|
const fallbackLabels = ['entity', 'relationship', 'document']
|
||||||
|
SearchHistoryManager.clearHistory()
|
||||||
|
await SearchHistoryManager.initializeWithDefaults(fallbackLabels)
|
||||||
|
} finally {
|
||||||
|
// Always clear the flag
|
||||||
|
shouldRefreshPopularLabelsRef.current = false
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Helper: Bump dropdown data to trigger refresh
|
||||||
|
const bumpDropdownData = useCallback(({ forceSelectKey = false } = {}) => {
|
||||||
|
setRefreshTrigger(prev => prev + 1)
|
||||||
|
if (forceSelectKey) {
|
||||||
|
setSelectKey(prev => prev + 1)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const fetchData = useCallback(
|
const fetchData = useCallback(
|
||||||
async (query?: string): Promise<string[]> => {
|
async (query?: string): Promise<string[]> => {
|
||||||
let results: string[] = [];
|
let results: string[] = [];
|
||||||
|
|
@ -115,6 +164,12 @@ const GraphLabels = () => {
|
||||||
currentLabel = '*'
|
currentLabel = '*'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scenario 1: Manual refresh - reload popular labels if flag is set (regardless of current label)
|
||||||
|
if (shouldRefreshPopularLabelsRef.current) {
|
||||||
|
await reloadPopularLabels()
|
||||||
|
bumpDropdownData({ forceSelectKey: true })
|
||||||
|
}
|
||||||
|
|
||||||
if (currentLabel && currentLabel !== '*') {
|
if (currentLabel && currentLabel !== '*') {
|
||||||
// Scenario 1: Has specific label, try to refresh current label
|
// Scenario 1: Has specific label, try to refresh current label
|
||||||
console.log(`Refreshing current label: ${currentLabel}`)
|
console.log(`Refreshing current label: ${currentLabel}`)
|
||||||
|
|
@ -135,7 +190,7 @@ const GraphLabels = () => {
|
||||||
console.log('Refreshing global data and popular labels')
|
console.log('Refreshing global data and popular labels')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Re-fetch popular labels and update search history
|
// Re-fetch popular labels and update search history (if not already done)
|
||||||
const popularLabels = await getPopularLabels(popularLabelsDefaultLimit)
|
const popularLabels = await getPopularLabels(popularLabelsDefaultLimit)
|
||||||
SearchHistoryManager.clearHistory()
|
SearchHistoryManager.clearHistory()
|
||||||
|
|
||||||
|
|
@ -173,7 +228,16 @@ const GraphLabels = () => {
|
||||||
} finally {
|
} finally {
|
||||||
setIsRefreshing(false)
|
setIsRefreshing(false)
|
||||||
}
|
}
|
||||||
}, [label])
|
}, [label, reloadPopularLabels, bumpDropdownData])
|
||||||
|
|
||||||
|
// Handle dropdown before open - reload popular labels if needed
|
||||||
|
const handleDropdownBeforeOpen = useCallback(async () => {
|
||||||
|
const currentLabel = useSettingsStore.getState().queryLabel
|
||||||
|
if (shouldRefreshPopularLabelsRef.current && (!currentLabel || currentLabel === '*')) {
|
||||||
|
await reloadPopularLabels()
|
||||||
|
bumpDropdownData()
|
||||||
|
}
|
||||||
|
}, [reloadPopularLabels, bumpDropdownData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
|
|
@ -196,6 +260,7 @@ const GraphLabels = () => {
|
||||||
searchInputClassName="max-h-8"
|
searchInputClassName="max-h-8"
|
||||||
triggerTooltip={t('graphPanel.graphLabels.selectTooltip')}
|
triggerTooltip={t('graphPanel.graphLabels.selectTooltip')}
|
||||||
fetcher={fetchData}
|
fetcher={fetchData}
|
||||||
|
onBeforeOpen={handleDropdownBeforeOpen}
|
||||||
renderOption={(item) => (
|
renderOption={(item) => (
|
||||||
<div className="truncate" title={item}>
|
<div className="truncate" title={item}>
|
||||||
{item}
|
{item}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ export interface AsyncSelectProps<T> {
|
||||||
value: string
|
value: string
|
||||||
/** Callback when selection changes */
|
/** Callback when selection changes */
|
||||||
onChange: (value: string) => void
|
onChange: (value: string) => void
|
||||||
|
/** Callback before opening the dropdown (async supported) */
|
||||||
|
onBeforeOpen?: () => void | Promise<void>
|
||||||
/** Accessibility label for the select field */
|
/** Accessibility label for the select field */
|
||||||
ariaLabel?: string
|
ariaLabel?: string
|
||||||
/** Placeholder text when no selection */
|
/** Placeholder text when no selection */
|
||||||
|
|
@ -83,6 +85,7 @@ export function AsyncSelect<T>({
|
||||||
searchPlaceholder,
|
searchPlaceholder,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
|
onBeforeOpen,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
className,
|
className,
|
||||||
triggerClassName,
|
triggerClassName,
|
||||||
|
|
@ -196,8 +199,18 @@ export function AsyncSelect<T>({
|
||||||
[selectedValue, onChange, clearable, options, getOptionValue]
|
[selectedValue, onChange, clearable, options, getOptionValue]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleOpenChange = useCallback(
|
||||||
|
async (newOpen: boolean) => {
|
||||||
|
if (newOpen && onBeforeOpen) {
|
||||||
|
await onBeforeOpen()
|
||||||
|
}
|
||||||
|
setOpen(newOpen)
|
||||||
|
},
|
||||||
|
[onBeforeOpen]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={handleOpenChange}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue