Skip to content

feat(desktop): channel hover state and right-click mark-unread context menu#578

Merged
wesbillman merged 5 commits into
mainfrom
channel-hover-state
May 14, 2026
Merged

feat(desktop): channel hover state and right-click mark-unread context menu#578
wesbillman merged 5 commits into
mainfrom
channel-hover-state

Conversation

@matt2e
Copy link
Copy Markdown
Collaborator

@matt2e matt2e commented May 14, 2026

Hover state shouldn't hide the unread status (previously the dot disappeared on hover)
Screenshot 2026-05-14 at 2 17 06 pm

Add context menu to mark a channel as unread:
Screenshot 2026-05-14 at 2 17 11 pm

Summary

  • Fix unread indicator visibility so it remains visible when hovering over a channel in the sidebar
  • Add right-click context menu on sidebar channels with a "Mark unread" action
  • Use a dedicated noopMarkUnread fallback and skip the ContextMenu wrapper when no mark-unread handler is available
  • Protect mark-unread rollback from being overwritten during state merges

🤖 Generated with Claude Code

matt2e and others added 5 commits May 14, 2026 12:27
Remove group-hover/menu-item:hidden from both channel and DM unread
indicator spans so the unread dot remains visible while browsing the
sidebar with the mouse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Add a right-click context menu to all sidebar channel items (streams,
forums, and DMs) using @radix-ui/react-context-menu. The menu exposes a
"Mark unread" action that rolls back the NIP-RS read-state timestamp to
just before the channel's last message, causing the unread indicator to
reappear. The change syncs across devices via the existing NIP-RS
publish mechanism.

- Install @radix-ui/react-context-menu and create a shared context-menu
  component mirroring the existing dropdown-menu wrapper
- Add markContextUnread to ReadStateManager (bypasses the monotonic
  advance guard to allow rolling back the timestamp)
- Expose markChannelUnread through useReadState and useUnreadChannels
- Wrap SidebarMenuItem in ContextMenu in both ChannelGroupSection and
  SidebarSection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
… fallback

Add a separate noopMarkUnread function instead of reusing noopMarkRead
for the markContextUnread fallback, making the noop fallback table
self-documenting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Add a forcedContexts set to ReadStateManager that prevents the
monotonic merge guard in mergeEvents and handleIncomingEvent from
overwriting intentionally rolled-back timestamps. Without this, the
pre-publish fetch always restored the old higher timestamp from the
relay, silently undoing every mark-unread action after the 5-second
debounce.

The set is populated in markContextUnread and drained after a
successful publish, at which point the relay already has the new
lower timestamp and protection is no longer needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Only wrap sidebar channel items in Radix ContextMenu when
onMarkChannelUnread is provided. Previously, the ContextMenu wrapper
was always rendered even without content, which caused Radix to
suppress the browser's native right-click menu and show nothing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
@matt2e matt2e requested a review from wesbillman as a code owner May 14, 2026 05:31
@wesbillman wesbillman merged commit f0549b5 into main May 14, 2026
15 checks passed
@wesbillman wesbillman deleted the channel-hover-state branch May 14, 2026 15:34
tlongwell-block added a commit that referenced this pull request May 14, 2026
Pulls in 8 commits from origin/main:
- 1858e98 fix(desktop): drive unread badges from live subscription, not refetched lastMessageAt (#581)
- 9e76a08 fix(desktop): refine header scaling and shadow (#573)
- b74ec95 fix(desktop): keep day dividers below header (#574)
- aad564b Move agent activity below composer (#579)
- bda98da docs(nips): NIP-AE — Agent Engrams (#575)
- 1b87a09 refactor: extract shared @mention resolver into sprout-sdk (#580)
- 2ee7356 fix: add default-run to sprout-relay so `cargo run -p sprout-relay` works (#577)
- f0549b5 feat(desktop): channel hover state and right-click mark-unread context menu (#578)

No conflicts.

* origin/main:
  fix(desktop): drive unread badges from live subscription, not refetched lastMessageAt (#581)
  fix(desktop): refine header scaling and shadow (#573)
  fix(desktop): keep day dividers below header (#574)
  Move agent activity below composer (#579)
  docs(nips): NIP-AE — Agent Engrams (#575)
  refactor: extract shared @mention resolver into sprout-sdk (#580)
  fix: add default-run to sprout-relay so `cargo run -p sprout-relay` works (#577)
  feat(desktop): channel hover state and right-click mark-unread context menu (#578)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants