useStore to isListening status

This commit is contained in:
hjlarry 2025-09-30 10:48:38 +08:00
parent d72a3e1879
commit fd2f0df097
9 changed files with 79 additions and 47 deletions

View File

@ -215,21 +215,24 @@ export const useWorkflowRun = () => {
const {
setWorkflowRunningData,
setIsListening,
} = workflowStore.getState()
if (runMode === 'webhook') {
setIsListening(true)
setWorkflowRunningData({
result: {
status: WorkflowRunningStatus.Listening,
status: WorkflowRunningStatus.Running,
inputs_truncated: false,
process_data_truncated: false,
outputs_truncated: false,
},
tracing: [],
resultText: 'Listening for webhook call...',
resultText: '',
})
}
else {
setIsListening(false)
setWorkflowRunningData({
result: {
status: WorkflowRunningStatus.Running,
@ -260,9 +263,15 @@ export const useWorkflowRun = () => {
abortControllerRef.current = null
}
const clearListeningState = () => {
const state = workflowStore.getState()
state.setIsListening(false)
}
const wrappedOnError = (params: any) => {
clearAbortController()
handleWorkflowFailed()
clearListeningState()
if (onError)
onError(params)
@ -270,6 +279,7 @@ export const useWorkflowRun = () => {
const wrappedOnCompleted: IOtherOptions['onCompleted'] = async (hasError?: boolean, errorMessage?: string) => {
clearAbortController()
clearListeningState()
if (onCompleted)
onCompleted(hasError, errorMessage)
}
@ -289,6 +299,7 @@ export const useWorkflowRun = () => {
onWorkflowStarted(params)
},
onWorkflowFinished: (params) => {
clearListeningState()
handleWorkflowFinished(params)
if (onWorkflowFinished)
@ -468,9 +479,11 @@ export const useWorkflowRun = () => {
},
tracing: [],
})
setIsListening(false)
return
}
setIsListening(false)
handleStream(
response,
baseSseOptions.onData ?? noop,
@ -518,6 +531,7 @@ export const useWorkflowRun = () => {
},
tracing: [],
})
setIsListening(false)
}
}
@ -561,7 +575,7 @@ export const useWorkflowRun = () => {
abortControllerRef.current.abort()
abortControllerRef.current = null
const { setWorkflowRunningData } = workflowStore.getState()
const { setWorkflowRunningData, setIsListening } = workflowStore.getState()
setWorkflowRunningData({
result: {
status: WorkflowRunningStatus.Stopped,
@ -572,6 +586,7 @@ export const useWorkflowRun = () => {
tracing: [],
resultText: '',
})
setIsListening(false)
}, [workflowStore])
const handleRestoreFromPublishedWorkflow = useCallback((publishedWorkflow: VersionHistory) => {

View File

@ -28,9 +28,9 @@ const RunMode = ({
const { handleStopRun } = useWorkflowRun()
const { validateBeforeRun } = useWorkflowRunValidation()
const workflowRunningData = useStore(s => s.workflowRunningData)
const isListening = useStore(s => s.isListening)
const status = workflowRunningData?.result.status
const isListening = status === WorkflowRunningStatus.Listening
const isRunning = status === WorkflowRunningStatus.Running || isListening
const dynamicOptions = useDynamicTestRunOptions()

View File

@ -5,11 +5,9 @@ import { useStore } from '../store'
import { useWorkflowUpdate } from '../hooks'
import { useHooksStore } from '../hooks-store'
import { formatWorkflowRunIdentifier } from '../utils'
import { WorkflowRunningStatus } from '../types'
const Record = () => {
const historyWorkflowData = useStore(s => s.historyWorkflowData)
const workflowRunningData = useStore(s => s.workflowRunningData)
const { handleUpdateWorkflowCanvas } = useWorkflowUpdate()
const getWorkflowRunAndTraceUrl = useHooksStore(s => s.getWorkflowRunAndTraceUrl)
@ -22,9 +20,6 @@ const Record = () => {
})
}, [handleUpdateWorkflowCanvas])
const currentStatus = workflowRunningData?.result.status
const activeTab = currentStatus === WorkflowRunningStatus.Listening ? 'DETAIL' : undefined
return (
<div className='flex h-full w-[400px] flex-col rounded-l-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'>
<div className='system-xl-semibold flex items-center justify-between p-4 pb-0 text-text-primary'>
@ -34,8 +29,6 @@ const Record = () => {
runDetailUrl={getWorkflowRunAndTraceUrl(historyWorkflowData?.id).runUrl}
tracingListUrl={getWorkflowRunAndTraceUrl(historyWorkflowData?.id).traceUrl}
getResultCallback={handleResultCallback}
activeTab={activeTab}
statusHint={currentStatus}
/>
</div>
)

View File

@ -31,6 +31,7 @@ const WorkflowPreview = () => {
const { t } = useTranslation()
const { handleCancelDebugAndPreviewPanel } = useWorkflowInteractions()
const workflowRunningData = useStore(s => s.workflowRunningData)
const isListening = useStore(s => s.isListening)
const showInputsPanel = useStore(s => s.showInputsPanel)
const workflowCanvasWidth = useStore(s => s.workflowCanvasWidth)
const panelWidth = useStore(s => s.previewPanelWidth)
@ -47,16 +48,16 @@ const WorkflowPreview = () => {
setCurrentTab('INPUT')
}, [showDebugAndPreviewPanel, showInputsPanel])
useEffect(() => {
if (isListening)
switchTab('DETAIL')
}, [isListening])
useEffect(() => {
const status = workflowRunningData?.result.status
if (!workflowRunningData)
return
if (status === WorkflowRunningStatus.Listening) {
switchTab('DETAIL')
return
}
if ((status === WorkflowRunningStatus.Succeeded || status === WorkflowRunningStatus.Failed) && !workflowRunningData.resultText && !workflowRunningData.result.files?.length)
switchTab('DETAIL')
}, [workflowRunningData])

View File

@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'
import OutputPanel from './output-panel'
import ResultPanel from './result-panel'
import StatusPanel from './status'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import TracingPanel from './tracing-panel'
import cn from '@/utils/classnames'
import { ToastContext } from '@/app/components/base/toast'
@ -13,14 +14,14 @@ import Loading from '@/app/components/base/loading'
import { fetchRunDetail, fetchTracingList } from '@/service/log'
import type { NodeTracing } from '@/types/workflow'
import type { WorkflowRunDetailResponse } from '@/models/log'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { useStore } from '../store'
export type RunProps = {
hideResult?: boolean
activeTab?: 'RESULT' | 'DETAIL' | 'TRACING'
getResultCallback?: (result: WorkflowRunDetailResponse) => void
runDetailUrl: string
tracingListUrl: string
statusHint?: string
}
const RunPanel: FC<RunProps> = ({
@ -29,7 +30,6 @@ const RunPanel: FC<RunProps> = ({
getResultCallback,
runDetailUrl,
tracingListUrl,
statusHint,
}) => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
@ -37,6 +37,7 @@ const RunPanel: FC<RunProps> = ({
const [loading, setLoading] = useState<boolean>(true)
const [runDetail, setRunDetail] = useState<WorkflowRunDetailResponse>()
const [list, setList] = useState<NodeTracing[]>([])
const isListening = useStore(s => s.isListening)
const executor = useMemo(() => {
if (runDetail?.created_by_role === 'account')
@ -90,19 +91,17 @@ const RunPanel: FC<RunProps> = ({
tracingListUrl && await getTracingList()
}
useEffect(() => {
if (isListening)
setCurrentTab('DETAIL')
}, [isListening])
useEffect(() => {
// fetch data
if (runDetailUrl && tracingListUrl)
getData()
}, [runDetailUrl, tracingListUrl])
const derivedStatus = runDetail?.status ?? statusHint
useEffect(() => {
if (derivedStatus === WorkflowRunningStatus.Listening && currentTab !== 'DETAIL')
setCurrentTab('DETAIL')
}, [currentTab, derivedStatus])
const [height, setHeight] = useState(0)
const ref = useRef<HTMLDivElement>(null)
@ -174,9 +173,9 @@ const RunPanel: FC<RunProps> = ({
exceptionCounts={runDetail.exceptions_count}
/>
)}
{!loading && currentTab === 'DETAIL' && !runDetail && derivedStatus === WorkflowRunningStatus.Listening && (
{!loading && currentTab === 'DETAIL' && !runDetail && isListening && (
<StatusPanel
status={WorkflowRunningStatus.Listening}
status={WorkflowRunningStatus.Running}
/>
)}
{!loading && currentTab === 'TRACING' && (

View File

@ -14,7 +14,6 @@ const StatusContainer: FC<Props> = ({
children,
}) => {
const { theme } = useTheme()
const isActive = status === 'running' || status === 'listening'
return (
<div
@ -35,9 +34,9 @@ const StatusContainer: FC<Props> = ({
status === 'exception' && 'border-[rgba(247,144,9,0.8)] bg-workflow-display-warning-bg bg-[url(~@/app/components/workflow/run/assets/bg-line-warning.svg)] text-text-destructive',
status === 'exception' && theme === Theme.light && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.5),inset_0_1px_3px_0_rgba(0,0,0,0.12),inset_0_2px_24px_0_rgba(247,144,9,0.2),0_1px_2px_0_rgba(9,9,11,0.05),0_0_0_1px_rgba(0,0,0,0.05)]',
status === 'exception' && theme === Theme.dark && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.12),inset_0_1px_3px_0_rgba(0,0,0,0.4),inset_0_2px_24px_0_rgba(247,144,9,0.25),0_1px_2px_0_rgba(0,0,0,0.1),0_0_0_1px_rgba(24, 24, 27, 0.95)]',
isActive && 'border-[rgba(11,165,236,0.8)] bg-workflow-display-normal-bg bg-[url(~@/app/components/workflow/run/assets/bg-line-running.svg)] text-util-colors-blue-light-blue-light-600',
isActive && theme === Theme.light && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.5),inset_0_1px_3px_0_rgba(0,0,0,0.12),inset_0_2px_24px_0_rgba(11,165,236,0.2),0_1px_2px_0_rgba(9,9,11,0.05),0_0_0_1px_rgba(0,0,0,0.05)]',
isActive && theme === Theme.dark && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.12),inset_0_1px_3px_0_rgba(0,0,0,0.4),inset_0_2px_24px_0_rgba(11,165,236,0.25),0_1px_2px_0_rgba(0,0,0,0.1),0_0_0_1px_rgba(24, 24, 27, 0.95)]',
status === 'running' && 'border-[rgba(11,165,236,0.8)] bg-workflow-display-normal-bg bg-[url(~@/app/components/workflow/run/assets/bg-line-running.svg)] text-util-colors-blue-light-blue-light-600',
status === 'running' && theme === Theme.light && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.5),inset_0_1px_3px_0_rgba(0,0,0,0.12),inset_0_2px_24px_0_rgba(11,165,236,0.2),0_1px_2px_0_rgba(9,9,11,0.05),0_0_0_1px_rgba(0,0,0,0.05)]',
status === 'running' && theme === Theme.dark && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.12),inset_0_1px_3px_0_rgba(0,0,0,0.4),inset_0_2px_24px_0_rgba(11,165,236,0.25),0_1px_2px_0_rgba(0,0,0,0.1),0_0_0_1px_rgba(24, 24, 27, 0.95)]',
)}
>
<div className={cn(

View File

@ -5,7 +5,7 @@ import cn from '@/utils/classnames'
import Indicator from '@/app/components/header/indicator'
import StatusContainer from '@/app/components/workflow/run/status-container'
import { useDocLink } from '@/context/i18n'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { useStore } from '../store'
type ResultProps = {
status: string
@ -24,7 +24,35 @@ const StatusPanel: FC<ResultProps> = ({
}) => {
const { t } = useTranslation()
const docLink = useDocLink()
const isActive = status === WorkflowRunningStatus.Running || status === WorkflowRunningStatus.Listening
const isListening = useStore(s => s.isListening)
if (isListening) {
return (
<StatusContainer status={'running'}>
<div className='flex'>
<div className='max-w-[120px] flex-[33%]'>
<div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t('runLog.resultPanel.status')}</div>
<div className='system-xs-semibold-uppercase flex items-center gap-1 text-util-colors-blue-light-blue-light-600'>
<Indicator color='blue' />
<span>LISTENING</span>
</div>
</div>
<div className='max-w-[152px] flex-[33%]'>
<div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t('runLog.resultPanel.time')}</div>
<div className='system-sm-medium flex items-center gap-1 text-text-secondary'>
<div className='h-2 w-16 rounded-sm bg-text-quaternary' />
</div>
</div>
<div className='flex-[33%]'>
<div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t('runLog.resultPanel.tokens')}</div>
<div className='system-sm-medium flex items-center gap-1 text-text-secondary'>
<div className='h-2 w-20 rounded-sm bg-text-quaternary' />
</div>
</div>
</div>
</StatusContainer>
)
}
return (
<StatusContainer status={status}>
@ -41,21 +69,15 @@ const StatusPanel: FC<ResultProps> = ({
status === 'partial-succeeded' && 'text-util-colors-green-green-600',
status === 'failed' && 'text-util-colors-red-red-600',
status === 'stopped' && 'text-util-colors-warning-warning-600',
isActive && 'text-util-colors-blue-light-blue-light-600',
status === 'running' && 'text-util-colors-blue-light-blue-light-600',
)}
>
{status === WorkflowRunningStatus.Running && (
{status === 'running' && (
<>
<Indicator color={'blue'} />
<span>Running</span>
</>
)}
{status === WorkflowRunningStatus.Listening && (
<>
<Indicator color={'blue'} />
<span>Listening</span>
</>
)}
{status === 'succeeded' && (
<>
<Indicator color={'green'} />
@ -91,10 +113,10 @@ const StatusPanel: FC<ResultProps> = ({
<div className='max-w-[152px] flex-[33%]'>
<div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t('runLog.resultPanel.time')}</div>
<div className='system-sm-medium flex items-center gap-1 text-text-secondary'>
{isActive && (
{status === 'running' && (
<div className='h-2 w-16 rounded-sm bg-text-quaternary' />
)}
{!isActive && (
{status !== 'running' && (
<span>{time ? `${time?.toFixed(3)}s` : '-'}</span>
)}
</div>
@ -102,10 +124,10 @@ const StatusPanel: FC<ResultProps> = ({
<div className='flex-[33%]'>
<div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t('runLog.resultPanel.tokens')}</div>
<div className='system-sm-medium flex items-center gap-1 text-text-secondary'>
{isActive && (
{status === 'running' && (
<div className='h-2 w-20 rounded-sm bg-text-quaternary' />
)}
{!isActive && (
{status !== 'running' && (
<span>{`${tokens || 0} Tokens`}</span>
)}
</div>

View File

@ -13,6 +13,8 @@ type PreviewRunningData = WorkflowRunningData & {
export type WorkflowSliceShape = {
workflowRunningData?: PreviewRunningData
setWorkflowRunningData: (workflowData: PreviewRunningData) => void
isListening: boolean
setIsListening: (listening: boolean) => void
clipboardElements: Node[]
setClipboardElements: (clipboardElements: Node[]) => void
selection: null | { x1: number; y1: number; x2: number; y2: number }
@ -36,6 +38,8 @@ export type WorkflowSliceShape = {
export const createWorkflowSlice: StateCreator<WorkflowSliceShape> = set => ({
workflowRunningData: undefined,
setWorkflowRunningData: workflowRunningData => set(() => ({ workflowRunningData })),
isListening: false,
setIsListening: listening => set(() => ({ isListening: listening })),
clipboardElements: [],
setClipboardElements: clipboardElements => set(() => ({ clipboardElements })),
selection: null,

View File

@ -344,7 +344,6 @@ export type NodeDefault<T = {}> = {
export type OnSelectBlock = (type: BlockEnum, pluginDefaultValue?: PluginDefaultValue) => void
export enum WorkflowRunningStatus {
Listening = 'listening',
Waiting = 'waiting',
Running = 'running',
Succeeded = 'succeeded',