import React, { useState } from 'react'; import { Room } from 'matrix-js-sdk'; import { Icon, Icons, Modal, Overlay, OverlayBackdrop, OverlayCenter, Text, color, config, } from 'folds'; import FocusTrap from 'focus-trap-react'; import { useSetting } from '../../state/hooks/settings'; import { settingsAtom } from '../../state/settings'; import { getMemberName } from '../../utils/room'; import { UserAvatar } from '../user-avatar'; import { StackedAvatar } from '../stacked-avatar'; import { EventReaders } from '../event-readers'; import { stopPropagation } from '../../utils/keyboard'; import { useModalStyle } from '../../hooks/useModalStyle'; import { useMemberAvatar } from '../../hooks/useMemberAvatar'; import { useRoomMembersChange } from '../../hooks/useRoomMemberChange'; import { MobileTouchTarget } from '../../styles/mobile.css'; import * as css from './ReadReceiptAvatars.css'; const MAX_DISPLAY = 5; function ReceiptStackedAvatar({ room, userId }: { room: Room; userId: string }) { const { name, avatarUrl } = useMemberAvatar(room, userId); return ( } /> ); } export function ReadReceiptAvatars({ room, eventId, userIds, }: { room: Room; eventId: string; userIds: string[]; }) { const [open, setOpen] = useState(false); const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal'); const modalStyle = useModalStyle(360); // The tooltip names below are read from room member state at render time. // Re-render on a member-state change of any displayed reader so a display-name // update shows live (each avatar handles its own via useMemberAvatar). Uses the // shared member-change store — one global listener for the whole app (PERF-3). useRoomMembersChange(room.roomId, userIds); if (userIds.length === 0) return null; const displayed = userIds.slice(0, MAX_DISPLAY); const extra = userIds.length - MAX_DISPLAY; const tooltipNames = userIds .slice(0, 5) .map((id) => getMemberName(room, id)) .join(', ') + (extra > 0 ? ` +${extra} more` : ''); return ( <> }> setOpen(false), clickOutsideDeactivates: true, escapeDeactivates: stopPropagation, }} > setOpen(false)} /> ); }