switch to skills tab, keep ws connected and ensure has leader

This commit is contained in:
hjlarry
2026-01-27 10:22:05 +08:00
parent 585e11a1fc
commit c4e5eba6c3
6 changed files with 166 additions and 30 deletions

View File

@ -71,11 +71,13 @@ export class CollaborationManager {
private leaderId: string | null = null
private cursors: Record<string, CursorPosition> = {}
private nodePanelPresence: NodePanelPresenceMap = {}
private onlineUsers: OnlineUser[] = []
private activeConnections = new Set<string>()
private isUndoRedoInProgress = false
private pendingInitialSync = false
private rejoinInProgress = false
private pendingGraphImportEmit = false
private graphViewActive: boolean | null = null
private getActiveSocket(): Socket | null {
if (!this.currentAppId)
@ -83,6 +85,10 @@ export class CollaborationManager {
return webSocketClient.getSocket(this.currentAppId)
}
setReactFlowStore(store: ReactFlowStore | null): void {
this.reactFlowStore = store
}
private handleSessionUnauthorized = (): void => {
if (this.rejoinInProgress)
return
@ -495,6 +501,7 @@ export class CollaborationManager {
this.reactFlowStore = null
this.cursors = {}
this.nodePanelPresence = {}
this.onlineUsers = []
this.isUndoRedoInProgress = false
this.rejoinInProgress = false
@ -600,11 +607,15 @@ export class CollaborationManager {
}
onCursorUpdate(callback: (cursors: Record<string, CursorPosition>) => void): () => void {
return this.eventEmitter.on('cursors', callback)
const off = this.eventEmitter.on('cursors', callback)
callback({ ...this.cursors })
return off
}
onOnlineUsersUpdate(callback: (users: OnlineUser[]) => void): () => void {
return this.eventEmitter.on('onlineUsers', callback)
const off = this.eventEmitter.on('onlineUsers', callback)
callback([...this.onlineUsers])
return off
}
onWorkflowUpdate(callback: (update: { appId: string, timestamp: number }) => void): () => void {
@ -656,6 +667,18 @@ export class CollaborationManager {
})
}
emitGraphViewActive(isActive: boolean): void {
this.graphViewActive = isActive
if (!this.currentAppId || !webSocketClient.isConnected(this.currentAppId))
return
this.sendCollaborationEvent({
type: 'graph_view_active',
data: { active: isActive },
timestamp: Date.now(),
})
}
onUndoRedoStateChange(callback: (state: { canUndo: boolean, canRedo: boolean }) => void): () => void {
return this.eventEmitter.on('undoRedoStateChange', callback)
}
@ -1081,6 +1104,7 @@ export class CollaborationManager {
if (data.leader && typeof data.leader === 'string')
this.leaderId = data.leader
this.onlineUsers = data.users
this.eventEmitter.emit('onlineUsers', data.users)
this.eventEmitter.emit('cursors', { ...this.cursors })
}
@ -1115,6 +1139,8 @@ export class CollaborationManager {
socket.on('connect', () => {
this.eventEmitter.emit('stateChange', { isConnected: true })
this.pendingInitialSync = true
if (this.graphViewActive !== null)
this.emitGraphViewActive(this.graphViewActive)
})
socket.on('disconnect', () => {

View File

@ -51,7 +51,7 @@ export function useCollaboration(appId: string, reactFlowStore?: ReactFlowStore)
const initCollaboration = async () => {
try {
const id = await collaborationManager.connect(appId, reactFlowStore)
const id = await collaborationManager.connect(appId)
if (isUnmounted) {
collaborationManager.disconnect(id)
return
@ -100,7 +100,17 @@ export function useCollaboration(appId: string, reactFlowStore?: ReactFlowStore)
if (connectionId)
collaborationManager.disconnect(connectionId)
}
}, [appId, reactFlowStore, isCollaborationEnabled])
}, [appId, isCollaborationEnabled])
useEffect(() => {
if (!reactFlowStore)
return
collaborationManager.setReactFlowStore(reactFlowStore)
return () => {
collaborationManager.setReactFlowStore(null)
}
}, [reactFlowStore])
const prevIsConnected = useRef(false)
useEffect(() => {

View File

@ -62,6 +62,7 @@ export type CollaborationEventType
| 'comments_update'
| 'node_panel_presence'
| 'app_publish_update'
| 'graph_view_active'
| 'graph_resync_request'
| 'workflow_restore_request'
| 'workflow_restore_intent'