Skip to content

Commit e462ba5

Browse files
authored
Merge pull request #565 from Chris0Jeky/fix/523-presence-panel-flicker
fix: resolve collaborators presence panel flicker (#523)
2 parents eb49629 + 60fec70 commit e462ba5

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
lines changed

frontend/taskdeck-web/src/tests/views/BoardView.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ import { mount } from '@vue/test-utils'
33
import { reactive } from 'vue'
44
import BoardView from '../../views/BoardView.vue'
55

6+
const mockSessionStore = reactive<{ userId: string | null; username: string | null }>({
7+
userId: 'user-abc',
8+
username: 'alice',
9+
})
10+
11+
vi.mock('../../store/sessionStore', () => ({
12+
useSessionStore: () => mockSessionStore,
13+
}))
14+
615
const routerMock = vi.hoisted(() => ({
716
push: vi.fn(),
817
}))
@@ -122,6 +131,8 @@ describe('BoardView', () => {
122131
vi.clearAllMocks()
123132
localStorage.clear()
124133
routeMock.params.id = 'board-1'
134+
mockSessionStore.userId = 'user-abc'
135+
mockSessionStore.username = 'alice'
125136
mockBoardStore.currentBoard = {
126137
id: 'board-1',
127138
name: 'Ops Board',
@@ -228,4 +239,29 @@ describe('BoardView', () => {
228239

229240
expect(addCardToggleMock).toHaveBeenCalledTimes(1)
230241
})
242+
243+
it('seeds presence with the current user immediately on mount so the panel never flickers to empty', async () => {
244+
// Verify setBoardPresenceMembers is called with the current user before
245+
// fetchBoard and realtime.start resolve — no empty-state window (#523).
246+
mountView()
247+
await waitForUi()
248+
249+
const firstCall = mockBoardStore.setBoardPresenceMembers.mock.calls[0]
250+
expect(firstCall).toBeDefined()
251+
expect(firstCall[0]).toEqual([
252+
{ userId: 'user-abc', displayName: 'alice', editingCardId: null },
253+
])
254+
})
255+
256+
it('seeds presence with empty array when no user session is active', async () => {
257+
mockSessionStore.userId = null
258+
mockSessionStore.username = null
259+
260+
mountView()
261+
await waitForUi()
262+
263+
const firstCall = mockBoardStore.setBoardPresenceMembers.mock.calls[0]
264+
expect(firstCall).toBeDefined()
265+
expect(firstCall[0]).toEqual([])
266+
})
231267
})

frontend/taskdeck-web/src/views/BoardView.vue

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { onBeforeUnmount, onMounted, ref, computed, watch } from 'vue'
33
import { useRoute, useRouter } from 'vue-router'
44
import { useBoardStore } from '../store/boardStore'
5+
import { useSessionStore } from '../store/sessionStore'
56
import { useKeyboardShortcuts } from '../composables/useKeyboardShortcuts'
67
import { createBoardRealtimeController } from '../composables/useBoardRealtime'
78
import { useBoardDragDrop } from '../composables/useBoardDragDrop'
@@ -20,6 +21,7 @@ import { isClientOnboardingDemoBoardName } from '../utils/boardDemo'
2021
const route = useRoute()
2122
const router = useRouter()
2223
const boardStore = useBoardStore()
24+
const sessionStore = useSessionStore()
2325
2426
const newColumnName = ref('')
2527
const showColumnForm = ref(false)
@@ -33,6 +35,17 @@ const presenceMembers = ref<BoardPresenceMember[]>([])
3335
3436
const boardLoadPerf = usePerformanceMark('board-load')
3537
38+
/**
39+
* Build a presence entry for the current authenticated user so the panel
40+
* shows immediately on mount, without waiting for the async SignalR
41+
* BoardJoined push event. This eliminates the empty-state flicker.
42+
*/
43+
function currentUserPresenceSeed(): BoardPresenceMember[] {
44+
const uid = sessionStore.userId
45+
if (!uid) return []
46+
return [{ userId: uid, displayName: sessionStore.username ?? null, editingCardId: null }]
47+
}
48+
3649
3750
const boardId = ref(route.params.id as string)
3851
const realtime = createBoardRealtimeController({
@@ -81,11 +94,19 @@ const {
8194
resetSelection,
8295
} = useBoardKeyboardNav(sortedColumns)
8396
97+
function applyPresenceSeed() {
98+
const seed = currentUserPresenceSeed()
99+
presenceMembers.value = seed
100+
boardStore.setBoardPresenceMembers(seed)
101+
}
102+
84103
onMounted(async () => {
85104
boardLoadPerf.start()
86105
try {
87-
presenceMembers.value = []
88-
boardStore.setBoardPresenceMembers([])
106+
// Seed presence with the current user immediately so the panel never
107+
// shows "No active collaborators" while waiting for the SignalR
108+
// BoardJoined push event (fixes #523 flicker).
109+
applyPresenceSeed()
89110
boardStore.setEditingCard(null)
90111
await boardStore.fetchBoard(boardId.value)
91112
await realtime.start(boardId.value)
@@ -106,8 +127,8 @@ watch(
106127
107128
boardId.value = nextBoardId
108129
resetSelection()
109-
presenceMembers.value = []
110-
boardStore.setBoardPresenceMembers([])
130+
// Seed with current user on board switch for the same reason as onMounted.
131+
applyPresenceSeed()
111132
boardStore.setEditingCard(null)
112133
113134
try {

0 commit comments

Comments
 (0)