import React from 'react'; import { useTranslation } from 'react-i18next'; import { connect, useSelector } from 'react-redux'; import { makeStyles } from 'tss-react/mui'; import { IReduxState } from '../../../app/types'; import participantsPaneTheme from '../../../base/components/themes/participantsPaneTheme.json'; import { getParticipantById, isScreenShareParticipant } from '../../../base/participants/functions'; import Input from '../../../base/ui/components/web/Input'; import useContextMenu from '../../../base/ui/hooks/useContextMenu.web'; import { normalizeAccents } from '../../../base/util/strings.web'; import { getBreakoutRooms, getCurrentRoomId, isInBreakoutRoom } from '../../../breakout-rooms/functions'; import { isButtonEnabled, showOverflowDrawer } from '../../../toolbox/functions.web'; import { iAmVisitor } from '../../../visitors/functions'; import { getSortedParticipantIds, isCurrentRoomRenamable, shouldRenderInviteButton } from '../../functions'; import { useParticipantDrawer } from '../../hooks'; import RenameButton from '../breakout-rooms/components/web/RenameButton'; import { InviteButton } from './InviteButton'; import MeetingParticipantContextMenu from './MeetingParticipantContextMenu'; import MeetingParticipantItems from './MeetingParticipantItems'; const useStyles = makeStyles()(theme => { return { headingW: { color: theme.palette.warning02 }, heading: { color: theme.palette.text02, ...theme.typography.bodyShortBold, marginBottom: theme.spacing(3), [`@media(max-width: ${participantsPaneTheme.MD_BREAKPOINT})`]: { ...theme.typography.bodyShortBoldLarge } }, search: { margin: `${theme.spacing(3)} 0`, '& input': { textAlign: 'center', paddingRight: '16px' } } }; }); interface IProps { currentRoom?: { jid: string; name: string; }; overflowDrawer?: boolean; participantsCount?: number; searchString: string; setSearchString: (newValue: string) => void; showInviteButton?: boolean; sortedParticipantIds?: Array; } /** * Renders the MeetingParticipantList component. * NOTE: This component is not using useSelector on purpose. The child components MeetingParticipantItem * and MeetingParticipantContextMenu are using connect. Having those mixed leads to problems. * When this one was using useSelector and the other two were not -the other two were re-rendered before this one was * re-rendered, so when participant is leaving, we first re-render the item and menu components, * throwing errors (closing the page) before removing those components for the participant that left. * * @returns {ReactNode} - The component. */ function MeetingParticipants({ currentRoom, overflowDrawer, participantsCount, searchString, setSearchString, showInviteButton, sortedParticipantIds = [] }: IProps) { const { t } = useTranslation(); const [ lowerMenu, , toggleMenu, menuEnter, menuLeave, raiseContext ] = useContextMenu(); const [ drawerParticipant, closeDrawer, openDrawerForParticipant ] = useParticipantDrawer(); // FIXME: // It seems that useTranslation is not very scalable. Unmount 500 components that have the useTranslation hook is // taking more than 10s. To workaround the issue we need to pass the texts as props. This is temporary and dirty // solution!!! // One potential proper fix would be to use react-window component in order to lower the number of components // mounted. const participantActionEllipsisLabel = t('participantsPane.actions.moreParticipantOptions'); const youText = t('chat.you'); const isBreakoutRoom = useSelector(isInBreakoutRoom); const _isCurrentRoomRenamable = useSelector(isCurrentRoomRenamable); const { classes: styles } = useStyles(); return ( <> { t('participantsPane.title') }
{currentRoom?.name ? `${currentRoom.name} (${participantsCount})` : t('participantsPane.headings.participantsList', { count: participantsCount })} { currentRoom?.name && _isCurrentRoomRenamable && }
{showInviteButton && }
); } /** * Maps (parts of) the redux state to the associated props for this component. * * @param {Object} state - The Redux state. * @param {Object} ownProps - The own props of the component. * @private * @returns {IProps} */ function _mapStateToProps(state: IReduxState) { let sortedParticipantIds: any = getSortedParticipantIds(state); const _iAmVisitor = iAmVisitor(state); // Filter out the virtual screenshare participants since we do not want them to be displayed as separate // participants in the participants pane. // Filter local participant when in visitor mode sortedParticipantIds = sortedParticipantIds.filter((id: any) => { const participant = getParticipantById(state, id); if (_iAmVisitor && participant?.local) { return false; } return !isScreenShareParticipant(participant); }); const participantsCount = sortedParticipantIds.length; const showInviteButton = shouldRenderInviteButton(state) && isButtonEnabled('invite', state); const overflowDrawer = showOverflowDrawer(state); const currentRoomId = getCurrentRoomId(state); const currentRoom = getBreakoutRooms(state)[currentRoomId]; return { currentRoom, overflowDrawer, participantsCount, showInviteButton, sortedParticipantIds }; } export default connect(_mapStateToProps)(MeetingParticipants);