import { Box, config, Icon, Icons, Menu, MenuItem, PopOut, RectCords, Text } from 'folds'; import { CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; import React, { useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { Room } from 'matrix-js-sdk'; import { UserAvatar } from '../../components/user-avatar'; import { getMemberAvatarMxc, getMemberDisplayName } from '../../utils/room'; import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; import { StackedAvatar } from '../../components/stacked-avatar'; import { useOpenUserRoomProfile } from '../../state/hooks/userRoomProfile'; import { stopPropagation } from '../../utils/keyboard'; import { CallEmbed } from '../../plugins/call/CallEmbed'; import * as css from './styles.css'; type ParticipantMenuProps = { anchor: RectCords; name: string; userId: string; room: Room; callEmbed?: CallEmbed; onClose: () => void; profileCords: DOMRect; }; function ParticipantMenu({ anchor, name, userId, room, callEmbed, onClose, profileCords, }: ParticipantMenuProps) { const openUserProfile = useOpenUserRoomProfile(); const handleViewProfile = () => { onClose(); openUserProfile(room.roomId, undefined, userId, profileCords, 'Top'); }; const handleFocusCamera = () => { onClose(); callEmbed?.control.focusCameraParticipant(userId); }; return ( {name} {callEmbed && ( } onClick={handleFocusCamera} > Focus camera )} } onClick={handleViewProfile} > View profile } > {/* PopOut requires a JSX child even if we anchor externally */} ); } type MemberGlanceProps = { room: Room; members: CallMembership[]; speakers: Set; callEmbed?: CallEmbed; max?: number; }; export function MemberGlance({ room, members, speakers, callEmbed, max = 6 }: MemberGlanceProps) { const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); const [menuState, setMenuState] = useState<{ anchor: RectCords; profileCords: DOMRect; userId: string; name: string; } | null>(null); const visibleMembers = members.slice(0, max); const remainingCount = max && members.length > max ? members.length - max : 0; return ( <> {visibleMembers.map((callMember) => { const { userId } = callMember; if (!userId) return null; const name = getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId) ?? userId; const avatarMxc = getMemberAvatarMxc(room, userId); const avatarUrl = avatarMxc ? (mxcUrlToHttp(mx, avatarMxc, useAuthentication, 96, 96) ?? undefined) : undefined; return ( { const rect = evt.currentTarget.getBoundingClientRect(); setMenuState({ anchor: rect, profileCords: rect, userId, name, }); }} > } /> ); })} {remainingCount > 0 && ( +{remainingCount} )} {menuState && ( setMenuState(null)} /> )} ); }