mirror of
https://github.com/langgenius/dify.git
synced 2026-04-15 18:06:36 +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
|
||||
broadcastCurrentGraph: () => void
|
||||
requestInitialSyncIfNeeded: () => void
|
||||
cleanupNodePanelPresence: (activeClientIds: Set<string>, activeUserIds: Set<string>) => void
|
||||
cleanupNodePanelPresence: (activeClientIds: Set<string>) => void
|
||||
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',
|
||||
status: 'triggered' | 'skipped' | 'applied' | 'queued' | 'emitted' | 'snapshot',
|
||||
@ -426,6 +426,65 @@ describe('CollaborationManager socket and subscription behavior', () => {
|
||||
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', () => {
|
||||
const { manager, internals } = setupManagerWithDoc()
|
||||
const rafSpy = vi.spyOn(globalThis, 'requestAnimationFrame').mockImplementation((callback: FrameRequestCallback) => {
|
||||
|
||||
@ -416,16 +416,14 @@ export class CollaborationManager {
|
||||
this.eventEmitter.emit('nodePanelPresence', this.getNodePanelPresenceSnapshot())
|
||||
}
|
||||
|
||||
private cleanupNodePanelPresence(activeClientIds: Set<string>, activeUserIds: Set<string>): void {
|
||||
private cleanupNodePanelPresence(activeClientIds: Set<string>): void {
|
||||
let hasChanges = false
|
||||
|
||||
Object.entries(this.nodePanelPresence).forEach(([nodeId, viewers]) => {
|
||||
Object.keys(viewers).forEach((clientId) => {
|
||||
const viewer = viewers[clientId]
|
||||
const clientActive = activeClientIds.has(clientId)
|
||||
const userActive = viewer?.userId ? activeUserIds.has(viewer.userId) : false
|
||||
|
||||
if (!clientActive && !userActive) {
|
||||
if (!clientActive) {
|
||||
delete viewers[clientId]
|
||||
hasChanges = true
|
||||
}
|
||||
@ -1513,7 +1511,7 @@ export class CollaborationManager {
|
||||
delete this.cursors[userId]
|
||||
})
|
||||
|
||||
this.cleanupNodePanelPresence(onlineClientIds, onlineUserIds)
|
||||
this.cleanupNodePanelPresence(onlineClientIds)
|
||||
|
||||
// Update leader information
|
||||
if (data.leader && typeof data.leader === 'string')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user