mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 09:28:04 +08:00
Merge branch 'feat/collaboration' into feat/collaboration2
This commit is contained in:
@ -1,13 +1,16 @@
|
||||
import { useCallback } from 'react'
|
||||
import { produce } from 'immer'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { useNodesReadOnly } from '@/app/components/workflow/hooks/use-workflow'
|
||||
import { useSerialAsyncCallback } from '@/app/components/workflow/hooks/use-serial-async-callback'
|
||||
import { syncWorkflowDraft } from '@/service/workflow'
|
||||
import { type WorkflowDraftFeaturesPayload, syncWorkflowDraft } from '@/service/workflow'
|
||||
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { API_PREFIX } from '@/config'
|
||||
import { useWorkflowRefreshDraft } from '.'
|
||||
import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
|
||||
export const useNodesSyncDraft = () => {
|
||||
const store = useStoreApi()
|
||||
@ -15,6 +18,8 @@ export const useNodesSyncDraft = () => {
|
||||
const featuresStore = useFeaturesStore()
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const { handleRefreshWorkflowDraft } = useWorkflowRefreshDraft()
|
||||
const params = useParams()
|
||||
const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode)
|
||||
|
||||
const getPostParams = useCallback(() => {
|
||||
const {
|
||||
@ -52,7 +57,16 @@ export const useNodesSyncDraft = () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
const viewport = { x, y, zoom }
|
||||
const featuresPayload: WorkflowDraftFeaturesPayload = {
|
||||
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,
|
||||
}
|
||||
|
||||
return {
|
||||
url: `/apps/${appId}/workflows/draft`,
|
||||
@ -60,33 +74,44 @@ export const useNodesSyncDraft = () => {
|
||||
graph: {
|
||||
nodes: producedNodes,
|
||||
edges: producedEdges,
|
||||
viewport,
|
||||
},
|
||||
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,
|
||||
viewport: {
|
||||
x,
|
||||
y,
|
||||
zoom,
|
||||
},
|
||||
},
|
||||
features: featuresPayload,
|
||||
environment_variables: environmentVariables,
|
||||
conversation_variables: conversationVariables,
|
||||
hash: syncWorkflowDraftHash,
|
||||
_is_collaborative: isCollaborationEnabled,
|
||||
},
|
||||
}
|
||||
}, [store, featuresStore, workflowStore])
|
||||
}, [store, featuresStore, workflowStore, isCollaborationEnabled])
|
||||
|
||||
const syncWorkflowDraftWhenPageClose = useCallback(() => {
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
// Check leader status at sync time
|
||||
const currentIsLeader = isCollaborationEnabled ? collaborationManager.getIsLeader() : true
|
||||
|
||||
// Only allow leader to sync data
|
||||
if (isCollaborationEnabled && !currentIsLeader) {
|
||||
console.log('Not leader, skipping sync on page close')
|
||||
return
|
||||
}
|
||||
|
||||
const postParams = getPostParams()
|
||||
|
||||
if (postParams)
|
||||
navigator.sendBeacon(`${API_PREFIX}${postParams.url}`, JSON.stringify(postParams.params))
|
||||
}, [getPostParams, getNodesReadOnly])
|
||||
if (postParams) {
|
||||
console.log('Leader syncing workflow draft on page close')
|
||||
navigator.sendBeacon(
|
||||
`${API_PREFIX}/apps/${params.appId}/workflows/draft`,
|
||||
JSON.stringify(postParams.params),
|
||||
)
|
||||
}
|
||||
}, [getPostParams, params.appId, getNodesReadOnly, isCollaborationEnabled])
|
||||
|
||||
const performSync = useCallback(async (
|
||||
notRefreshWhenSyncError?: boolean,
|
||||
@ -95,9 +120,24 @@ export const useNodesSyncDraft = () => {
|
||||
onError?: () => void
|
||||
onSettled?: () => void
|
||||
},
|
||||
forceUpload?: boolean,
|
||||
) => {
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
// Check leader status at sync time
|
||||
const currentIsLeader = isCollaborationEnabled ? collaborationManager.getIsLeader() : true
|
||||
|
||||
// If not leader and not forcing upload, request the leader to sync
|
||||
if (isCollaborationEnabled && !currentIsLeader && !forceUpload) {
|
||||
console.log('Not leader, requesting leader to sync workflow draft')
|
||||
if (isCollaborationEnabled)
|
||||
collaborationManager.emitSyncRequest()
|
||||
callback?.onSettled?.()
|
||||
return
|
||||
}
|
||||
|
||||
console.log(forceUpload ? 'Force uploading workflow draft' : 'Leader performing workflow draft sync')
|
||||
const postParams = getPostParams()
|
||||
|
||||
if (postParams) {
|
||||
@ -105,17 +145,31 @@ export const useNodesSyncDraft = () => {
|
||||
setSyncWorkflowDraftHash,
|
||||
setDraftUpdatedAt,
|
||||
} = workflowStore.getState()
|
||||
|
||||
// Add force_upload parameter if needed
|
||||
const finalParams = {
|
||||
...postParams.params,
|
||||
...(forceUpload && { force_upload: true }),
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await syncWorkflowDraft(postParams)
|
||||
const res = await syncWorkflowDraft({
|
||||
url: postParams.url,
|
||||
params: finalParams,
|
||||
})
|
||||
setSyncWorkflowDraftHash(res.hash)
|
||||
setDraftUpdatedAt(res.updated_at)
|
||||
console.log('Leader successfully synced workflow draft')
|
||||
callback?.onSuccess?.()
|
||||
}
|
||||
catch (error: any) {
|
||||
console.error('Leader failed to sync workflow draft:', error)
|
||||
if (error && error.json && !error.bodyUsed) {
|
||||
error.json().then((err: any) => {
|
||||
if (err.code === 'draft_workflow_not_sync' && !notRefreshWhenSyncError)
|
||||
if (err.code === 'draft_workflow_not_sync' && !notRefreshWhenSyncError) {
|
||||
console.error('draft_workflow_not_sync', err)
|
||||
handleRefreshWorkflowDraft()
|
||||
}
|
||||
})
|
||||
}
|
||||
callback?.onError?.()
|
||||
@ -124,7 +178,7 @@ export const useNodesSyncDraft = () => {
|
||||
callback?.onSettled?.()
|
||||
}
|
||||
}
|
||||
}, [workflowStore, getPostParams, getNodesReadOnly, handleRefreshWorkflowDraft])
|
||||
}, [workflowStore, getPostParams, getNodesReadOnly, handleRefreshWorkflowDraft, isCollaborationEnabled])
|
||||
|
||||
const doSyncWorkflowDraft = useSerialAsyncCallback(performSync, getNodesReadOnly)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user