| | |
| | | import { useCallback } from 'react' |
| | | import produce from 'immer' |
| | | import { useStoreApi } from 'reactflow' |
| | | import { useParams } from 'next/navigation' |
| | | import { |
| | | useStore, |
| | | useWorkflowStore, |
| | | } from '../store' |
| | | import { BlockEnum } from '../types' |
| | | import { useWorkflowUpdate } from '../hooks' |
| | | import { |
| | | useNodesReadOnly, |
| | | } from './use-workflow' |
| | | import { useHooksStore } from '@/app/components/workflow/hooks-store' |
| | | import { syncWorkflowDraft } from '@/service/workflow' |
| | | import { useFeaturesStore } from '@/app/components/base/features/hooks' |
| | | import { API_PREFIX } from '@/config' |
| | | |
| | | export const useNodesSyncDraft = () => { |
| | | const store = useStoreApi() |
| | | const workflowStore = useWorkflowStore() |
| | | const featuresStore = useFeaturesStore() |
| | | const { getNodesReadOnly } = useNodesReadOnly() |
| | | const { handleRefreshWorkflowDraft } = useWorkflowUpdate() |
| | | const debouncedSyncWorkflowDraft = useStore(s => s.debouncedSyncWorkflowDraft) |
| | | const doSyncWorkflowDraft = useHooksStore(s => s.doSyncWorkflowDraft) |
| | | const syncWorkflowDraftWhenPageClose = useHooksStore(s => s.syncWorkflowDraftWhenPageClose) |
| | | const params = useParams() |
| | | |
| | | const handleSyncWorkflowDraft = useCallback(( |
| | | sync?: boolean, |
| | | notRefreshWhenSyncError?: boolean, |
| | | callback?: { |
| | | onSuccess?: () => void |
| | | onError?: () => void |
| | | onSettled?: () => void |
| | | }, |
| | | ) => { |
| | | const getPostParams = useCallback(() => { |
| | | const { |
| | | getNodes, |
| | | edges, |
| | | transform, |
| | | } = store.getState() |
| | | const [x, y, zoom] = transform |
| | | const { |
| | | appId, |
| | | conversationVariables, |
| | | environmentVariables, |
| | | syncWorkflowDraftHash, |
| | | } = workflowStore.getState() |
| | | |
| | | if (appId) { |
| | | const nodes = getNodes() |
| | | const hasStartNode = nodes.find(node => node.data.type === BlockEnum.Start) |
| | | |
| | | if (!hasStartNode) |
| | | return |
| | | |
| | | const features = featuresStore!.getState().features |
| | | const producedNodes = produce(nodes, (draft) => { |
| | | draft.forEach((node) => { |
| | | Object.keys(node.data).forEach((key) => { |
| | | if (key.startsWith('_')) |
| | | delete node.data[key] |
| | | }) |
| | | }) |
| | | }) |
| | | const producedEdges = produce(edges, (draft) => { |
| | | draft.forEach((edge) => { |
| | | Object.keys(edge.data).forEach((key) => { |
| | | if (key.startsWith('_')) |
| | | delete edge.data[key] |
| | | }) |
| | | }) |
| | | }) |
| | | return { |
| | | url: `/apps/${appId}/workflows/draft`, |
| | | params: { |
| | | graph: { |
| | | nodes: producedNodes, |
| | | edges: producedEdges, |
| | | viewport: { |
| | | x, |
| | | y, |
| | | zoom, |
| | | }, |
| | | }, |
| | | features: { |
| | | opening_statement: features.opening?.enabled ? (features.opening?.opening_statement || '') : '', |
| | | suggested_questions: features.opening?.enabled ? (features.opening?.suggested_questions || []) : [], |
| | | suggested_questions_after_answer: features.suggested, |
| | | text_to_speech: features.text2speech, |
| | | speech_to_text: features.speech2text, |
| | | retriever_resource: features.citation, |
| | | sensitive_word_avoidance: features.moderation, |
| | | file_upload: features.file, |
| | | }, |
| | | environment_variables: environmentVariables, |
| | | conversation_variables: conversationVariables, |
| | | hash: syncWorkflowDraftHash, |
| | | }, |
| | | } |
| | | } |
| | | }, [store, featuresStore, workflowStore]) |
| | | |
| | | const syncWorkflowDraftWhenPageClose = useCallback(() => { |
| | | if (getNodesReadOnly()) |
| | | return |
| | | const postParams = getPostParams() |
| | | |
| | | if (postParams) { |
| | | navigator.sendBeacon( |
| | | `${API_PREFIX}/apps/${params.appId}/workflows/draft?_token=${localStorage.getItem('console_token')}`, |
| | | JSON.stringify(postParams.params), |
| | | ) |
| | | } |
| | | }, [getPostParams, params.appId, getNodesReadOnly]) |
| | | |
| | | const doSyncWorkflowDraft = useCallback(async (notRefreshWhenSyncError?: boolean) => { |
| | | if (getNodesReadOnly()) |
| | | return |
| | | const postParams = getPostParams() |
| | | |
| | | if (postParams) { |
| | | const { |
| | | setSyncWorkflowDraftHash, |
| | | setDraftUpdatedAt, |
| | | } = workflowStore.getState() |
| | | try { |
| | | const res = await syncWorkflowDraft(postParams) |
| | | setSyncWorkflowDraftHash(res.hash) |
| | | setDraftUpdatedAt(res.updated_at) |
| | | } |
| | | catch (error: any) { |
| | | if (error && error.json && !error.bodyUsed) { |
| | | error.json().then((err: any) => { |
| | | if (err.code === 'draft_workflow_not_sync' && !notRefreshWhenSyncError) |
| | | handleRefreshWorkflowDraft() |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | }, [workflowStore, getPostParams, getNodesReadOnly, handleRefreshWorkflowDraft]) |
| | | |
| | | const handleSyncWorkflowDraft = useCallback((sync?: boolean, notRefreshWhenSyncError?: boolean) => { |
| | | if (getNodesReadOnly()) |
| | | return |
| | | |
| | | if (sync) |
| | | doSyncWorkflowDraft(notRefreshWhenSyncError, callback) |
| | | doSyncWorkflowDraft(notRefreshWhenSyncError) |
| | | else |
| | | debouncedSyncWorkflowDraft(doSyncWorkflowDraft) |
| | | }, [debouncedSyncWorkflowDraft, doSyncWorkflowDraft, getNodesReadOnly]) |