From a475531b2b3f9cc8426baccbce46d33cec2818d6 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Sat, 19 Sep 2026 12:18:20 -0400 Subject: [PATCH] feat(calls): 'N Live' pill names who is in the call on hover/focus (#157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tooltip (folds TooltipProvider, 300 ms) on the room-list Live badge listing the call members' display names — deduped per user, capped at 6 + 'and N more' — plus an aria-label with the same text so keyboard/screen-reader users get it too (the badge is focusable). Names come from the same MatrixRTC memberships that produce the count; no new data. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA --- src/app/features/room-nav/RoomNavItem.tsx | 48 ++++++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/app/features/room-nav/RoomNavItem.tsx b/src/app/features/room-nav/RoomNavItem.tsx index 1c84f57ba..0334cf163 100644 --- a/src/app/features/room-nav/RoomNavItem.tsx +++ b/src/app/features/room-nav/RoomNavItem.tsx @@ -30,6 +30,8 @@ import { RectCords, Badge, Spinner, + Tooltip, + TooltipProvider, } from 'folds'; import { useFocusWithin, useHover } from 'react-aria'; import FocusTrap from 'focus-trap-react'; @@ -41,7 +43,12 @@ import isYesterday from 'dayjs/plugin/isYesterday'; import { NavItem, NavItemContent, NavItemOptions, NavLink } from '../../components/nav'; import { UnreadBadge, UnreadBadgeCenter } from '../../components/unread-badge'; import { RoomAvatar, RoomIcon } from '../../components/room-avatar'; -import { getDirectRoomAvatarUrl, getRoomAvatarUrl, getStateEvent } from '../../utils/room'; +import { + getDirectRoomAvatarUrl, + getMemberName, + getRoomAvatarUrl, + getStateEvent, +} from '../../utils/room'; import { nameInitials } from '../../utils/common'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useRoomUnread } from '../../state/hooks/unread'; @@ -690,6 +697,14 @@ function RoomNavItem_({ const optionsVisible = hover || !!menuAnchor; const callSession = useCallSession(room); const callMembers = useCallMembers(callSession); + const liveNames = useMemo(() => { + const names = Array.from( + new Set(callMembers.map((m) => m.sender).filter((u): u is string => !!u)), + ).map((u) => getMemberName(room, u)); + const shown = names.slice(0, 6); + const more = names.length - shown.length; + return more > 0 ? `${shown.join(', ')} and ${more} more` : shown.join(', '); + }, [callMembers, room]); const startCall = useCallStart(direct); const callEmbed = useCallEmbed(); // [Gitea #30] Same voice-limit check the call prescreen uses, so the sidebar @@ -853,11 +868,32 @@ function RoomNavItem_({ /> )} {callMembers.length > 0 && ( - - - {callMembers.length} Live - - + // [Gitea #157] The count alone doesn't tell you whether it's your + // squad — name the people on hover/focus (long-press on touch). + + {liveNames} + + } + > + {(tipRef) => ( + + + {callMembers.length} Live + + + )} + )}