Skip to content

fix: board header presence label uses username over email consistently (#683)#744

Merged
Chris0Jeky merged 3 commits intomainfrom
fix/683-board-header-presence-label
Apr 4, 2026
Merged

fix: board header presence label uses username over email consistently (#683)#744
Chris0Jeky merged 3 commits intomainfrom
fix/683-board-header-presence-label

Conversation

@Chris0Jeky
Copy link
Copy Markdown
Owner

Summary

  • Add normalizePresenceMembers() in BoardView.vue that replaces the current user's displayName with the locally-known sessionStore.username before applying any incoming SignalR presence snapshot
  • This prevents the idle→editing identity label switch where the server sends an email address (demo@taskdeck.local) while the local seed uses the short username (demo)
  • Other collaborators' display names are left unchanged
  • Closes UX-25: Board header presence label should not switch between username and email formats #683

Test plan

  • Unit test: current user email displayName is replaced with username in presence snapshot
  • Unit test: other members' displayNames are left unchanged
  • Unit test: snapshots for unrelated boards are ignored (regression guard)
  • All 1547 existing vitest tests pass
  • Manual: sign in as demo, open a board, verify label shows demo — open a card, verify label still shows demo (editing) not demo@taskdeck.local (editing)

…pshot

When a board presence snapshot arrives from the SignalR hub the server may
send the current user's email address as displayName.  The idle-state seed
uses the local session username, so the identity label was switching between
the two formats whenever the user opened a card for editing.

Introduce normalizePresenceMembers() in BoardView to replace the current
user's displayName with the locally-known username before updating the
presence list, making the label consistent across idle and editing states.

Closes #683
Cover three scenarios:
- current user email displayName is replaced with username in snapshot
- other members' displayNames are left unchanged
- snapshots for unrelated boards are ignored
Copilot AI review requested due to automatic review settings April 4, 2026 01:08
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Self-review findings

Logic correctness

  • normalizePresenceMembers guards on !currentUserId || !currentUsername and returns members unchanged — correct. The only edge case where normalization silently skips is if the session loses its username mid-session, which cannot happen while the user is actively on the board.
  • Matching is by userId, not by displayName string comparison, so the replacement is stable even if the server changes what it sends as displayName in the future.
  • Other collaborators are passed through unmodified — only the current user's entry is normalized.

Type safety

  • currentUsername is narrowed to string by the early-return guard before being assigned to displayName: string | null — no type coercion needed.

Bonus improvement

  • If the server sends null as displayName for the current user (instead of their email), the normalization now replaces it with username — better than the previous behavior where null would fall back to userId.slice(0, 8) in the template.

Minor fix applied post-review

  • Removed a stray blank line between the two presence helper functions (cosmetic only).

No real issues found.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the board presence handling in BoardView to keep the current user’s identity label stable (username vs email) when SignalR presence snapshots arrive, addressing the idle→editing label switch reported in #683.

Changes:

  • Added normalizePresenceMembers() in BoardView.vue to force the current user’s presence displayName to sessionStore.username when applying presence snapshots.
  • Updated presence snapshot handling to store/use the normalized member list.
  • Added vitest coverage for current-user normalization, “other member unchanged”, and “ignore other board snapshot” regression cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
frontend/taskdeck-web/src/views/BoardView.vue Normalizes current user presence displayName before updating local/store presence state.
frontend/taskdeck-web/src/tests/views/BoardView.spec.ts Adds unit tests validating normalization behavior and board scoping of snapshots.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses issue #683 by implementing display name normalization in BoardView.vue to ensure the current user's identity label remains consistent. It also adds unit tests to verify this normalization and ensure snapshots for other boards are ignored. Feedback suggests centralizing the normalization logic in a store or utility for better reusability.

Comment on lines +56 to +63
function normalizePresenceMembers(members: BoardPresenceMember[]): BoardPresenceMember[] {
const currentUserId = sessionStore.userId
const currentUsername = sessionStore.username
if (!currentUserId || !currentUsername) return members
return members.map((m) =>
m.userId === currentUserId ? { ...m, displayName: currentUsername } : m,
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While this normalization logic correctly addresses the issue in BoardView, it might be more robust to handle this transformation within the boardStore or a shared utility. If other views eventually display presence data, they would benefit from a centralized normalization of the current user's display name. However, for the current scope of fixing the idle-to-editing label switch in this component, this implementation is functional.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Adversarial review — PR #744 (fix/#683 board header presence label)

Summary

The fix is correct and safe. All 1544 frontend unit tests pass locally. Detailed findings below.


CI failures

API Integration fails on both ubuntu and windows — this is a pre-existing failure unrelated to this PR. The root cause is AuthControllerEdgeCaseTests.cs (lines 278, 291, 308): new AuthController(authService.Object, CreateGitHubSettings(false)) passes only 2 arguments but the current AuthController constructor requires 3 (AuthenticationService, GitHubOAuthSettings, IUserContext). The CI also fails on the main branch for the same commits that merged before this PR was opened — confirmed by checking gh run list --branch main. This is a pre-existing defect in the test suite and does not block this frontend-only PR.


Adversarial checklist

1. Does the fix handle sessionStore.username being null/empty?
Yes. normalizePresenceMembers guards with if (!currentUserId || !currentUsername) return members — passing the snapshot through unchanged when either value is absent. This is the correct fallback: don't replace with null, leave the server value as-is.

2. Could normalization run before sessionStore is hydrated?
No. normalizePresenceMembers is only invoked from the onPresenceChanged callback, which fires when a SignalR message arrives over the network — always after App.vue's onMounted has called restoreSession(). The closure references the Pinia store reactively, so it always reads the current value. No race condition.

3. Are the tests asserting the right thing?
Yes. The tests capture the onPresenceChanged callback via the createBoardRealtimeController mock, then fire it with synthetic snapshots and assert on setBoardPresenceMembers.mock.calls. The assertions verify exact member payloads including editingCardId preservation (important: confirms the fix doesn't drop the editing state). The "other boards ignored" test verifies the existing boardId guard, not just that the function runs.

One gap: there is no test for the username === null path through normalizePresenceMembers when a snapshot arrives. The existing "null session" test covers the seed/mount path only. This is low risk given the guard's simplicity, but adding it would complete coverage.

4. Are there other places in the codebase with the same bug?
BoardToolbar.vue is the only renderer of presence member labels (member.displayName || member.userId.slice(0, 8)). It receives presenceMembers as a prop passed from BoardView, which is now always the normalized array. boardStore.setBoardPresenceMembers has only one call-site that passes raw server data (the onPresenceChanged handler in BoardView) — that is now normalized before being passed. No other views or components consume boardPresenceMembers from the store directly for rendering.

5. Does the fix break multi-user boards?
No. normalizePresenceMembers only mutates the entry for userId === sessionStore.userId. Other collaborators' displayName values are passed through unchanged. Confirmed by the "leaves other members unchanged" test.

6. Does the fix affect the presence seed on mount?
The seed path (currentUserPresenceSeed) already uses sessionStore.username directly and is unaffected. The normalizePresenceMembers function is only called in the snapshot handler, not for the seed.


Minor findings (no action required)

  • Gemini bot suggestion (centralizing normalization in the store or a shared utility) is a reasonable long-term refactor but unnecessary for this scoped fix. setBoardPresenceMembers has exactly one upstream caller that deals with server data, so there is no current duplication risk.
  • The PR description says "1547 existing vitest tests pass" — local run shows 1544. This is a test count drift from prior merges and is not a problem.

Verdict

Approved for merge once the pre-existing AuthControllerEdgeCaseTests.cs CI failure is tracked. That failure existed on main before this branch was created and is unrelated to this change. The frontend fix is correct, well-tested, and does not regress any existing behavior.

@Chris0Jeky Chris0Jeky merged commit 57d9d67 into main Apr 4, 2026
21 of 23 checks passed
@Chris0Jeky Chris0Jeky deleted the fix/683-board-header-presence-label branch April 4, 2026 01:56
@github-project-automation github-project-automation bot moved this from Pending to Done in Taskdeck Execution Apr 4, 2026
Chris0Jeky added a commit that referenced this pull request Apr 4, 2026
Updates STATUS.md, TESTING_GUIDE.md, MANUAL_TEST_CHECKLIST.md, and
IMPLEMENTATION_MASTERPLAN.md to reflect the 9 PRs merged in the
2026-04-04 post-adversarial-review hardening wave.

- STATUS.md: adds new wave entry for PRs #741#756 covering product
  telemetry taxonomy, two bug fixes (#683/#744, #680/#754), six
  frontend regression test additions, and two backend webhook test
  additions; updates Last Updated date
- TESTING_GUIDE.md: updates frontend count from 1496 to 1592, backend
  estimate to ~3010+, combined total to ~4600+; marks #710 and #726 as
  delivered in wave table; adds resolved entries to Key Gaps section;
  updates telemetry taxonomy note to delivered; appends three new
  coverage sections for webhook HMAC, SSRF/delivery reliability, and
  frontend regression wave
- MANUAL_TEST_CHECKLIST.md: adds auth-flow toast check (Section A),
  WIP-limit dedup check (Section B), manual card provenance empty
  state check (Section B), board header presence label check (Section
  B), and D2 router auth guard and workspace state section
- IMPLEMENTATION_MASTERPLAN.md: adds item 128 for the post-adversarial
  hardening wave with all 9 issues, fixes, and wave progress update
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

UX-25: Board header presence label should not switch between username and email formats

2 participants