| useCallback, | useCallback, | ||||
| useEffect, | useEffect, | ||||
| useMemo, | useMemo, | ||||
| useState, | |||||
| } from 'react' | } from 'react' | ||||
| import dayjs from 'dayjs' | import dayjs from 'dayjs' | ||||
| import { uniqBy } from 'lodash-es' | import { uniqBy } from 'lodash-es' | ||||
| import { useContext } from 'use-context-selector' | import { useContext } from 'use-context-selector' | ||||
| import useSWR from 'swr' | |||||
| import produce from 'immer' | import produce from 'immer' | ||||
| import { | import { | ||||
| getIncomers, | getIncomers, | ||||
| fetchWorkflowDraft, | fetchWorkflowDraft, | ||||
| syncWorkflowDraft, | syncWorkflowDraft, | ||||
| } from '@/service/workflow' | } from '@/service/workflow' | ||||
| import type { FetchWorkflowDraftResponse } from '@/types/workflow' | |||||
| import { | import { | ||||
| fetchAllBuiltInTools, | fetchAllBuiltInTools, | ||||
| fetchAllCustomTools, | fetchAllCustomTools, | ||||
| } = useWorkflowTemplate() | } = useWorkflowTemplate() | ||||
| const { handleFetchAllTools } = useFetchToolsData() | const { handleFetchAllTools } = useFetchToolsData() | ||||
| const appDetail = useAppStore(state => state.appDetail)! | const appDetail = useAppStore(state => state.appDetail)! | ||||
| const { data, isLoading, error, mutate } = useSWR(`/apps/${appDetail.id}/workflows/draft`, fetchWorkflowDraft) | |||||
| const [data, setData] = useState<FetchWorkflowDraftResponse>() | |||||
| const [isLoading, setIsLoading] = useState(true) | |||||
| workflowStore.setState({ appId: appDetail.id }) | workflowStore.setState({ appId: appDetail.id }) | ||||
| const handleGetInitialWorkflowData = useCallback(async () => { | |||||
| try { | |||||
| const res = await fetchWorkflowDraft(`/apps/${appDetail.id}/workflows/draft`) | |||||
| setData(res) | |||||
| setIsLoading(false) | |||||
| } | |||||
| catch (error: any) { | |||||
| if (error && error.json && !error.bodyUsed && appDetail) { | |||||
| error.json().then((err: any) => { | |||||
| if (err.code === 'draft_workflow_not_exist') { | |||||
| workflowStore.setState({ notInitialWorkflow: true }) | |||||
| syncWorkflowDraft({ | |||||
| url: `/apps/${appDetail.id}/workflows/draft`, | |||||
| params: { | |||||
| graph: { | |||||
| nodes: nodesTemplate, | |||||
| edges: edgesTemplate, | |||||
| }, | |||||
| features: {}, | |||||
| }, | |||||
| }).then((res) => { | |||||
| workflowStore.getState().setDraftUpdatedAt(res.updated_at) | |||||
| handleGetInitialWorkflowData() | |||||
| }) | |||||
| } | |||||
| }) | |||||
| } | |||||
| } | |||||
| }, [appDetail, nodesTemplate, edgesTemplate, workflowStore]) | |||||
| useEffect(() => { | |||||
| handleGetInitialWorkflowData() | |||||
| }, []) | |||||
| const handleFetchPreloadData = useCallback(async () => { | const handleFetchPreloadData = useCallback(async () => { | ||||
| try { | try { | ||||
| const nodesDefaultConfigsData = await fetchNodesDefaultConfigs(`/apps/${appDetail?.id}/workflows/default-workflow-block-configs`) | const nodesDefaultConfigsData = await fetchNodesDefaultConfigs(`/apps/${appDetail?.id}/workflows/default-workflow-block-configs`) | ||||
| workflowStore.getState().setDraftUpdatedAt(data.updated_at) | workflowStore.getState().setDraftUpdatedAt(data.updated_at) | ||||
| }, [data, workflowStore]) | }, [data, workflowStore]) | ||||
| if (error && error.json && !error.bodyUsed && appDetail) { | |||||
| error.json().then((err: any) => { | |||||
| if (err.code === 'draft_workflow_not_exist') { | |||||
| workflowStore.setState({ notInitialWorkflow: true }) | |||||
| syncWorkflowDraft({ | |||||
| url: `/apps/${appDetail.id}/workflows/draft`, | |||||
| params: { | |||||
| graph: { | |||||
| nodes: nodesTemplate, | |||||
| edges: edgesTemplate, | |||||
| }, | |||||
| features: {}, | |||||
| }, | |||||
| }).then((res) => { | |||||
| workflowStore.getState().setDraftUpdatedAt(res.updated_at) | |||||
| mutate() | |||||
| }) | |||||
| } | |||||
| }) | |||||
| } | |||||
| return { | return { | ||||
| data, | data, | ||||
| isLoading, | isLoading, |
| useEdgesState, | useEdgesState, | ||||
| useNodesState, | useNodesState, | ||||
| useOnViewportChange, | useOnViewportChange, | ||||
| useReactFlow, | |||||
| } from 'reactflow' | } from 'reactflow' | ||||
| import type { Viewport } from 'reactflow' | import type { Viewport } from 'reactflow' | ||||
| import 'reactflow/dist/style.css' | import 'reactflow/dist/style.css' | ||||
| edges: originalEdges, | edges: originalEdges, | ||||
| viewport, | viewport, | ||||
| }) => { | }) => { | ||||
| const reactflow = useReactFlow() | |||||
| const [nodes, setNodes] = useNodesState(originalNodes) | const [nodes, setNodes] = useNodesState(originalNodes) | ||||
| const [edges, setEdges] = useEdgesState(originalEdges) | const [edges, setEdges] = useEdgesState(originalEdges) | ||||
| const showFeaturesPanel = useStore(state => state.showFeaturesPanel) | const showFeaturesPanel = useStore(state => state.showFeaturesPanel) | ||||
| } | } | ||||
| }) | }) | ||||
| useEffect(() => { | |||||
| setNodes(originalNodes) | |||||
| }, [originalNodes, setNodes]) | |||||
| useEffect(() => { | |||||
| setEdges(originalEdges) | |||||
| }, [originalEdges, setEdges]) | |||||
| useEffect(() => { | |||||
| if (viewport) | |||||
| reactflow.setViewport(viewport) | |||||
| }, [reactflow, viewport]) | |||||
| useEffect(() => { | useEffect(() => { | ||||
| setAutoFreeze(false) | setAutoFreeze(false) | ||||
| } = useEdgesInteractions() | } = useEdgesInteractions() | ||||
| const { | const { | ||||
| isValidConnection, | isValidConnection, | ||||
| enableShortcuts, | |||||
| disableShortcuts, | |||||
| } = useWorkflow() | } = useWorkflow() | ||||
| useOnViewportChange({ | useOnViewportChange({ | ||||
| edgeTypes={edgeTypes} | edgeTypes={edgeTypes} | ||||
| nodes={nodes} | nodes={nodes} | ||||
| edges={edges} | edges={edges} | ||||
| onPointerDown={enableShortcuts} | |||||
| onMouseLeave={disableShortcuts} | |||||
| onNodeDragStart={handleNodeDragStart} | onNodeDragStart={handleNodeDragStart} | ||||
| onNodeDrag={handleNodeDrag} | onNodeDrag={handleNodeDrag} | ||||
| onNodeDragStop={handleNodeDragStop} | onNodeDragStop={handleNodeDragStop} |
| import type { CommonNodeType } from '../types' | import type { CommonNodeType } from '../types' | ||||
| import { Panel as NodePanel } from '../nodes' | import { Panel as NodePanel } from '../nodes' | ||||
| import { useStore } from '../store' | import { useStore } from '../store' | ||||
| import { useIsChatMode } from '../hooks' | |||||
| import { | |||||
| useIsChatMode, | |||||
| useWorkflow, | |||||
| } from '../hooks' | |||||
| import DebugAndPreview from './debug-and-preview' | import DebugAndPreview from './debug-and-preview' | ||||
| import Record from './record' | import Record from './record' | ||||
| import WorkflowPreview from './workflow-preview' | import WorkflowPreview from './workflow-preview' | ||||
| const workflowRunningData = useStore(s => s.workflowRunningData) | const workflowRunningData = useStore(s => s.workflowRunningData) | ||||
| const historyWorkflowData = useStore(s => s.historyWorkflowData) | const historyWorkflowData = useStore(s => s.historyWorkflowData) | ||||
| const isRestoring = useStore(s => s.isRestoring) | const isRestoring = useStore(s => s.isRestoring) | ||||
| const { | |||||
| enableShortcuts, | |||||
| disableShortcuts, | |||||
| } = useWorkflow() | |||||
| const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal } = useAppStore(useShallow(state => ({ | const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal } = useAppStore(useShallow(state => ({ | ||||
| currentLogItem: state.currentLogItem, | currentLogItem: state.currentLogItem, | ||||
| setCurrentLogItem: state.setCurrentLogItem, | setCurrentLogItem: state.setCurrentLogItem, | ||||
| ]) | ]) | ||||
| return ( | return ( | ||||
| <div className='absolute top-14 right-0 bottom-2 flex z-10' key={`${isRestoring}`}> | |||||
| <div | |||||
| tabIndex={-1} | |||||
| className='absolute top-14 right-0 bottom-2 flex z-10 outline-none' | |||||
| onFocus={disableShortcuts} | |||||
| onBlur={enableShortcuts} | |||||
| key={`${isRestoring}`} | |||||
| > | |||||
| { | { | ||||
| showMessageLogModal && ( | showMessageLogModal && ( | ||||
| <MessageLogModal | <MessageLogModal |