mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
add ENABLE_COLLABORATION_MODE
This commit is contained in:
@ -45,26 +45,38 @@ const WorkflowMain = ({
|
||||
const reactFlow = useReactFlow()
|
||||
|
||||
const store = useStoreApi()
|
||||
const { startCursorTracking, stopCursorTracking, onlineUsers, cursors, isConnected } = useCollaboration(appId || '', store)
|
||||
const {
|
||||
startCursorTracking,
|
||||
stopCursorTracking,
|
||||
onlineUsers,
|
||||
cursors,
|
||||
isConnected,
|
||||
isEnabled: isCollaborationEnabled,
|
||||
} = useCollaboration(appId || '', store)
|
||||
const [myUserId, setMyUserId] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (isConnected)
|
||||
if (isCollaborationEnabled && isConnected)
|
||||
setMyUserId('current-user')
|
||||
}, [isConnected])
|
||||
else
|
||||
setMyUserId(null)
|
||||
}, [isCollaborationEnabled, isConnected])
|
||||
|
||||
const filteredCursors = Object.fromEntries(
|
||||
Object.entries(cursors).filter(([userId]) => userId !== myUserId),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCollaborationEnabled)
|
||||
return
|
||||
|
||||
if (containerRef.current)
|
||||
startCursorTracking(containerRef as React.RefObject<HTMLElement>, reactFlow)
|
||||
|
||||
return () => {
|
||||
stopCursorTracking()
|
||||
}
|
||||
}, [startCursorTracking, stopCursorTracking, reactFlow])
|
||||
}, [startCursorTracking, stopCursorTracking, reactFlow, isCollaborationEnabled])
|
||||
|
||||
const handleWorkflowDataUpdate = useCallback((payload: any) => {
|
||||
const {
|
||||
@ -128,7 +140,7 @@ const WorkflowMain = ({
|
||||
} = useWorkflowRun()
|
||||
|
||||
useEffect(() => {
|
||||
if (!appId) return
|
||||
if (!appId || !isCollaborationEnabled) return
|
||||
|
||||
const unsubscribe = collaborationManager.onVarsAndFeaturesUpdate(async (update: any) => {
|
||||
try {
|
||||
@ -141,11 +153,11 @@ const WorkflowMain = ({
|
||||
})
|
||||
|
||||
return unsubscribe
|
||||
}, [appId, handleWorkflowDataUpdate])
|
||||
}, [appId, handleWorkflowDataUpdate, isCollaborationEnabled])
|
||||
|
||||
// Listen for workflow updates from other users
|
||||
useEffect(() => {
|
||||
if (!appId) return
|
||||
if (!appId || !isCollaborationEnabled) return
|
||||
|
||||
const unsubscribe = collaborationManager.onWorkflowUpdate(async () => {
|
||||
console.log('Received workflow update from collaborator, fetching latest workflow data')
|
||||
@ -170,11 +182,11 @@ const WorkflowMain = ({
|
||||
})
|
||||
|
||||
return unsubscribe
|
||||
}, [appId, handleWorkflowDataUpdate, handleUpdateWorkflowCanvas])
|
||||
}, [appId, handleWorkflowDataUpdate, handleUpdateWorkflowCanvas, isCollaborationEnabled])
|
||||
|
||||
// Listen for sync requests from other users (only processed by leader)
|
||||
useEffect(() => {
|
||||
if (!appId) return
|
||||
if (!appId || !isCollaborationEnabled) return
|
||||
|
||||
const unsubscribe = collaborationManager.onSyncRequest(() => {
|
||||
console.log('Leader received sync request, performing sync')
|
||||
@ -182,7 +194,7 @@ const WorkflowMain = ({
|
||||
})
|
||||
|
||||
return unsubscribe
|
||||
}, [appId, doSyncWorkflowDraft])
|
||||
}, [appId, doSyncWorkflowDraft, isCollaborationEnabled])
|
||||
const {
|
||||
handleStartWorkflowRun,
|
||||
handleWorkflowStartRunInChatflow,
|
||||
|
||||
@ -14,6 +14,7 @@ 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()
|
||||
@ -22,6 +23,7 @@ export const useNodesSyncDraft = () => {
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const { handleRefreshWorkflowDraft } = useWorkflowRefreshDraft()
|
||||
const params = useParams()
|
||||
const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode)
|
||||
|
||||
const getPostParams = useCallback(() => {
|
||||
const {
|
||||
@ -86,21 +88,21 @@ export const useNodesSyncDraft = () => {
|
||||
environment_variables: environmentVariables,
|
||||
conversation_variables: conversationVariables,
|
||||
hash: syncWorkflowDraftHash,
|
||||
_is_collaborative: true,
|
||||
_is_collaborative: isCollaborationEnabled,
|
||||
},
|
||||
}
|
||||
}
|
||||
}, [store, featuresStore, workflowStore])
|
||||
}, [store, featuresStore, workflowStore, isCollaborationEnabled])
|
||||
|
||||
const syncWorkflowDraftWhenPageClose = useCallback(() => {
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
// Check leader status at sync time
|
||||
const currentIsLeader = collaborationManager.getIsLeader()
|
||||
const currentIsLeader = isCollaborationEnabled ? collaborationManager.getIsLeader() : true
|
||||
|
||||
// Only allow leader to sync data
|
||||
if (!currentIsLeader) {
|
||||
if (isCollaborationEnabled && !currentIsLeader) {
|
||||
console.log('Not leader, skipping sync on page close')
|
||||
return
|
||||
}
|
||||
@ -114,7 +116,7 @@ export const useNodesSyncDraft = () => {
|
||||
JSON.stringify(postParams.params),
|
||||
)
|
||||
}
|
||||
}, [getPostParams, params.appId, getNodesReadOnly])
|
||||
}, [getPostParams, params.appId, getNodesReadOnly, isCollaborationEnabled])
|
||||
|
||||
const doSyncWorkflowDraft = useCallback(async (
|
||||
notRefreshWhenSyncError?: boolean,
|
||||
@ -129,12 +131,13 @@ export const useNodesSyncDraft = () => {
|
||||
return
|
||||
|
||||
// Check leader status at sync time
|
||||
const currentIsLeader = collaborationManager.getIsLeader()
|
||||
const currentIsLeader = isCollaborationEnabled ? collaborationManager.getIsLeader() : true
|
||||
|
||||
// If not leader and not forcing upload, request the leader to sync
|
||||
if (!currentIsLeader && !forceUpload) {
|
||||
if (isCollaborationEnabled && !currentIsLeader && !forceUpload) {
|
||||
console.log('Not leader, requesting leader to sync workflow draft')
|
||||
collaborationManager.emitSyncRequest()
|
||||
if (isCollaborationEnabled)
|
||||
collaborationManager.emitSyncRequest()
|
||||
callback?.onSettled?.()
|
||||
return
|
||||
}
|
||||
@ -181,7 +184,7 @@ export const useNodesSyncDraft = () => {
|
||||
callback?.onSettled?.()
|
||||
}
|
||||
}
|
||||
}, [workflowStore, getPostParams, getNodesReadOnly, handleRefreshWorkflowDraft])
|
||||
}, [workflowStore, getPostParams, getNodesReadOnly, handleRefreshWorkflowDraft, isCollaborationEnabled])
|
||||
|
||||
return {
|
||||
doSyncWorkflowDraft,
|
||||
|
||||
@ -3,6 +3,7 @@ import type { ReactFlowInstance } from 'reactflow'
|
||||
import { collaborationManager } from '../core/collaboration-manager'
|
||||
import { CursorService } from '../services/cursor-service'
|
||||
import type { CollaborationState } from '../types/collaboration'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
|
||||
export function useCollaboration(appId: string, reactFlowStore?: any) {
|
||||
const [state, setState] = useState<Partial<CollaborationState & { isLeader: boolean }>>({
|
||||
@ -14,9 +15,19 @@ export function useCollaboration(appId: string, reactFlowStore?: any) {
|
||||
})
|
||||
|
||||
const cursorServiceRef = useRef<CursorService | null>(null)
|
||||
const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode)
|
||||
|
||||
useEffect(() => {
|
||||
if (!appId) return
|
||||
if (!appId || !isCollaborationEnabled) {
|
||||
setState({
|
||||
isConnected: false,
|
||||
onlineUsers: [],
|
||||
cursors: {},
|
||||
nodePanelPresence: {},
|
||||
isLeader: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
let connectionId: string | null = null
|
||||
let isUnmounted = false
|
||||
@ -75,9 +86,12 @@ export function useCollaboration(appId: string, reactFlowStore?: any) {
|
||||
if (connectionId)
|
||||
collaborationManager.disconnect(connectionId)
|
||||
}
|
||||
}, [appId, reactFlowStore])
|
||||
}, [appId, reactFlowStore, isCollaborationEnabled])
|
||||
|
||||
const startCursorTracking = (containerRef: React.RefObject<HTMLElement>, reactFlowInstance?: ReactFlowInstance) => {
|
||||
if (!isCollaborationEnabled || !cursorServiceRef.current)
|
||||
return
|
||||
|
||||
if (cursorServiceRef.current) {
|
||||
cursorServiceRef.current.startTracking(containerRef, (position) => {
|
||||
collaborationManager.emitCursorMove(position)
|
||||
@ -96,6 +110,7 @@ export function useCollaboration(appId: string, reactFlowStore?: any) {
|
||||
nodePanelPresence: state.nodePanelPresence || {},
|
||||
isLeader: state.isLeader || false,
|
||||
leaderId: collaborationManager.getLeaderId(),
|
||||
isEnabled: isCollaborationEnabled,
|
||||
startCursorTracking,
|
||||
stopCursorTracking,
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ const useAvatarUrls = (users: any[]) => {
|
||||
|
||||
const OnlineUsers = () => {
|
||||
const appId = useStore(s => s.appId)
|
||||
const { onlineUsers, cursors } = useCollaboration(appId as string)
|
||||
const { onlineUsers, cursors, isEnabled: isCollaborationEnabled } = useCollaboration(appId as string)
|
||||
const { userProfile } = useAppContext()
|
||||
const reactFlow = useReactFlow()
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false)
|
||||
@ -67,7 +67,7 @@ const OnlineUsers = () => {
|
||||
reactFlow.setCenter(cursor.x, cursor.y, { zoom: 1, duration: 800 })
|
||||
}
|
||||
|
||||
if (!onlineUsers || onlineUsers.length === 0)
|
||||
if (!isCollaborationEnabled || !onlineUsers || onlineUsers.length === 0)
|
||||
return null
|
||||
|
||||
// Display logic:
|
||||
|
||||
@ -39,6 +39,7 @@ export type SystemFeatures = {
|
||||
enable_email_code_login: boolean
|
||||
enable_email_password_login: boolean
|
||||
enable_social_oauth_login: boolean
|
||||
enable_collaboration_mode: boolean
|
||||
is_allow_create_workspace: boolean
|
||||
is_allow_register: boolean
|
||||
is_email_setup: boolean
|
||||
@ -75,6 +76,7 @@ export const defaultSystemFeatures: SystemFeatures = {
|
||||
enable_email_code_login: false,
|
||||
enable_email_password_login: false,
|
||||
enable_social_oauth_login: false,
|
||||
enable_collaboration_mode: false,
|
||||
is_allow_create_workspace: false,
|
||||
is_allow_register: false,
|
||||
is_email_setup: false,
|
||||
|
||||
Reference in New Issue
Block a user