mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 07:46:31 +08:00
fix ghost node panel presence cleanup
This commit is contained in:
parent
d3e9a34917
commit
e3b72df552
@ -72,7 +72,7 @@ type CollaborationManagerInternals = {
|
|||||||
emitGraphResyncRequest: () => void
|
emitGraphResyncRequest: () => void
|
||||||
broadcastCurrentGraph: () => void
|
broadcastCurrentGraph: () => void
|
||||||
requestInitialSyncIfNeeded: () => void
|
requestInitialSyncIfNeeded: () => void
|
||||||
cleanupNodePanelPresence: (activeClientIds: Set<string>, activeUserIds: Set<string>) => void
|
cleanupNodePanelPresence: (activeClientIds: Set<string>) => void
|
||||||
recordGraphSyncDiagnostic: (
|
recordGraphSyncDiagnostic: (
|
||||||
stage: 'nodes_subscribe' | 'edges_subscribe' | 'nodes_import_apply' | 'edges_import_apply' | 'schedule_graph_import_emit' | 'graph_import_emit' | 'start_import_log' | 'finalize_import_log',
|
stage: 'nodes_subscribe' | 'edges_subscribe' | 'nodes_import_apply' | 'edges_import_apply' | 'schedule_graph_import_emit' | 'graph_import_emit' | 'start_import_log' | 'finalize_import_log',
|
||||||
status: 'triggered' | 'skipped' | 'applied' | 'queued' | 'emitted' | 'snapshot',
|
status: 'triggered' | 'skipped' | 'applied' | 'queued' | 'emitted' | 'snapshot',
|
||||||
@ -426,6 +426,65 @@ describe('CollaborationManager socket and subscription behavior', () => {
|
|||||||
expect(errorSpy).toHaveBeenCalled()
|
expect(errorSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('removes stale node panel viewers by inactive client even when the same user is still online in another tab', () => {
|
||||||
|
const { manager, internals } = setupManagerWithDoc()
|
||||||
|
const socket = createMockSocket('socket-tab-b')
|
||||||
|
|
||||||
|
internals.nodePanelPresence = {
|
||||||
|
'n-1': {
|
||||||
|
'socket-tab-a': {
|
||||||
|
userId: 'u-1',
|
||||||
|
username: 'Alice',
|
||||||
|
clientId: 'socket-tab-a',
|
||||||
|
timestamp: 1,
|
||||||
|
},
|
||||||
|
'socket-tab-b': {
|
||||||
|
userId: 'u-1',
|
||||||
|
username: 'Alice',
|
||||||
|
clientId: 'socket-tab-b',
|
||||||
|
timestamp: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const presenceUpdates: NodePanelPresenceMap[] = []
|
||||||
|
manager.onNodePanelPresenceUpdate((presence) => {
|
||||||
|
presenceUpdates.push(presence)
|
||||||
|
})
|
||||||
|
|
||||||
|
internals.setupSocketEventListeners(socket as unknown as Socket)
|
||||||
|
|
||||||
|
socket.trigger('online_users', {
|
||||||
|
users: [{
|
||||||
|
user_id: 'u-1',
|
||||||
|
username: 'Alice',
|
||||||
|
avatar: '',
|
||||||
|
sid: 'socket-tab-b',
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(internals.nodePanelPresence).toEqual({
|
||||||
|
'n-1': {
|
||||||
|
'socket-tab-b': {
|
||||||
|
userId: 'u-1',
|
||||||
|
username: 'Alice',
|
||||||
|
clientId: 'socket-tab-b',
|
||||||
|
timestamp: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(presenceUpdates.at(-1)).toEqual({
|
||||||
|
'n-1': {
|
||||||
|
'socket-tab-b': {
|
||||||
|
userId: 'u-1',
|
||||||
|
username: 'Alice',
|
||||||
|
clientId: 'socket-tab-b',
|
||||||
|
timestamp: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('setupSubscriptions applies import updates and emits merged graph payload', () => {
|
it('setupSubscriptions applies import updates and emits merged graph payload', () => {
|
||||||
const { manager, internals } = setupManagerWithDoc()
|
const { manager, internals } = setupManagerWithDoc()
|
||||||
const rafSpy = vi.spyOn(globalThis, 'requestAnimationFrame').mockImplementation((callback: FrameRequestCallback) => {
|
const rafSpy = vi.spyOn(globalThis, 'requestAnimationFrame').mockImplementation((callback: FrameRequestCallback) => {
|
||||||
|
|||||||
@ -416,16 +416,14 @@ export class CollaborationManager {
|
|||||||
this.eventEmitter.emit('nodePanelPresence', this.getNodePanelPresenceSnapshot())
|
this.eventEmitter.emit('nodePanelPresence', this.getNodePanelPresenceSnapshot())
|
||||||
}
|
}
|
||||||
|
|
||||||
private cleanupNodePanelPresence(activeClientIds: Set<string>, activeUserIds: Set<string>): void {
|
private cleanupNodePanelPresence(activeClientIds: Set<string>): void {
|
||||||
let hasChanges = false
|
let hasChanges = false
|
||||||
|
|
||||||
Object.entries(this.nodePanelPresence).forEach(([nodeId, viewers]) => {
|
Object.entries(this.nodePanelPresence).forEach(([nodeId, viewers]) => {
|
||||||
Object.keys(viewers).forEach((clientId) => {
|
Object.keys(viewers).forEach((clientId) => {
|
||||||
const viewer = viewers[clientId]
|
|
||||||
const clientActive = activeClientIds.has(clientId)
|
const clientActive = activeClientIds.has(clientId)
|
||||||
const userActive = viewer?.userId ? activeUserIds.has(viewer.userId) : false
|
|
||||||
|
|
||||||
if (!clientActive && !userActive) {
|
if (!clientActive) {
|
||||||
delete viewers[clientId]
|
delete viewers[clientId]
|
||||||
hasChanges = true
|
hasChanges = true
|
||||||
}
|
}
|
||||||
@ -1513,7 +1511,7 @@ export class CollaborationManager {
|
|||||||
delete this.cursors[userId]
|
delete this.cursors[userId]
|
||||||
})
|
})
|
||||||
|
|
||||||
this.cleanupNodePanelPresence(onlineClientIds, onlineUserIds)
|
this.cleanupNodePanelPresence(onlineClientIds)
|
||||||
|
|
||||||
// Update leader information
|
// Update leader information
|
||||||
if (data.leader && typeof data.leader === 'string')
|
if (data.leader && typeof data.leader === 'string')
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user