Files
dify/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts
T

127 lines
5.2 KiB
TypeScript

import { useCallback } from 'react'
import { useStore as useAppStore } from '@/app/components/app/store'
import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager'
import { useWorkflowUpdate } from '@/app/components/workflow/hooks/use-workflow-update'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { fetchWorkflowDraft } from '@/service/workflow'
import { useWorkflowDraftGraphForCanvas } from './use-workflow-draft-graph-for-canvas'
type RefreshWorkflowDraftOptions = {
shouldApply?: () => boolean
syncToCollaboration?: boolean
resolveConflict?: boolean
builderRefresh?: boolean
}
export const useWorkflowRefreshDraft = () => {
const appDetail = useAppStore((s) => s.appDetail)
const workflowStore = useWorkflowStore()
const { handleUpdateWorkflowCanvas } = useWorkflowUpdate()
const { getWorkflowDraftGraphForCanvas } = useWorkflowDraftGraphForCanvas(appDetail?.mode)
const handleRefreshWorkflowDraft = useCallback(
(notUpdateCanvas?: boolean, options?: RefreshWorkflowDraftOptions) => {
if (workflowStore.getState().workflowDraftSyncPhase !== 'idle' && !options?.builderRefresh)
return Promise.resolve(false)
if (options?.shouldApply && !options.shouldApply()) return Promise.resolve(false)
// Background refreshes must not discard the local edits preserved after a conflict.
if (workflowStore.getState().hasWorkflowDraftConflict && !options?.resolveConflict)
return Promise.resolve(false)
// A visibility-triggered metadata refresh must not cancel a full graph reload.
if (notUpdateCanvas && workflowStore.getState().isSyncingWorkflowDraft)
return Promise.resolve(false)
const {
appId,
setSyncWorkflowDraftHash,
setDraftUpdatedAt,
setWorkflowDraftConflict,
setIsSyncingWorkflowDraft,
setEnvironmentVariables,
setEnvSecrets,
setConversationVariables,
setIsWorkflowDataLoaded,
invalidateWorkflowDraftSync,
} = workflowStore.getState()
const generation = invalidateWorkflowDraftSync()
const isCurrent = () => {
const state = workflowStore.getState()
return (
state.appId === appId &&
state.workflowDraftGeneration === generation &&
(options?.shouldApply?.() ?? true)
)
}
setIsSyncingWorkflowDraft(true)
return workflowStore.getState().enqueueWorkflowDraftOperation(async () => {
try {
if (!isCurrent()) return false
const response = await fetchWorkflowDraft(`/apps/${appId}/workflows/draft`)
if (!isCurrent()) return false
if (!notUpdateCanvas) {
// An explicit reload can restore the local draft while collaboration
// reconnects. Its graph recovery will restore the shared baseline.
const syncToCollaboration =
options?.syncToCollaboration ??
(!options?.resolveConflict || collaborationManager.canRestoreGraphFromCrdt())
const useServerWriter = syncToCollaboration && collaborationManager.isConnected()
let graph = getWorkflowDraftGraphForCanvas(response.graph)
if (useServerWriter) {
const synced = await collaborationManager.requestServerDraftSync()
if (!isCurrent() || synced.hash !== response.hash) return false
graph = {
...graph,
nodes: collaborationManager.getNodes(),
edges: collaborationManager.getEdges(),
}
}
const applied = handleUpdateWorkflowCanvas(graph, {
syncToCollaboration: syncToCollaboration && !useServerWriter,
features: response.features,
})
if (!applied) return false
// The hash is a baseline for this graph, never for a metadata-only refresh.
setSyncWorkflowDraftHash(response.hash)
}
setEnvSecrets(
(response.environment_variables || [])
.filter((env) => env.value_type === 'secret')
.reduce(
(acc, env) => {
if (typeof env.value === 'string') acc[env.id] = env.value
return acc
},
{} as Record<string, string>,
),
)
setEnvironmentVariables(
response.environment_variables?.map((env) =>
env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env,
) || [],
)
setConversationVariables(response.conversation_variables || [])
if (!notUpdateCanvas) {
setDraftUpdatedAt(response.updated_at)
setIsWorkflowDataLoaded(true)
setWorkflowDraftConflict(false)
if (options?.builderRefresh) workflowStore.getState().setWorkflowDraftSyncPhase('idle')
}
return true
} catch {
return false
} finally {
if (workflowStore.getState().workflowDraftGeneration === generation)
setIsSyncingWorkflowDraft(false)
}
})
},
[getWorkflowDraftGraphForCanvas, handleUpdateWorkflowCanvas, workflowStore],
)
return {
handleRefreshWorkflowDraft,
}
}