From 9816be47457f3ecfa0c106a67b7c3aefe3495d1a Mon Sep 17 00:00:00 2001 From: Robert Pintilii Date: Tue, 21 Dec 2021 14:51:39 +0200 Subject: [PATCH] fix(participants-pane) Make search work with breakout rooms (#10668) Web and native --- .../components/native/CollapsibleRoom.js | 13 +++-- .../components/web/CollapsibleRoom.js | 24 ++++++---- .../breakout-rooms/components/web/RoomList.js | 13 ++++- .../native/MeetingParticipantList.js | 48 ++++++------------- .../components/native/ParticipantsPane.js | 10 ++-- .../components/web/MeetingParticipantItem.js | 21 ++------ .../components/web/MeetingParticipants.js | 9 ++-- .../components/web/ParticipantsPane.js | 31 ++++++++++-- react/features/participants-pane/functions.js | 26 ++++++++++ 9 files changed, 121 insertions(+), 74 deletions(-) diff --git a/react/features/breakout-rooms/components/native/CollapsibleRoom.js b/react/features/breakout-rooms/components/native/CollapsibleRoom.js index 525301894..ffe3197ee 100644 --- a/react/features/breakout-rooms/components/native/CollapsibleRoom.js +++ b/react/features/breakout-rooms/components/native/CollapsibleRoom.js @@ -7,6 +7,7 @@ import { useDispatch } from 'react-redux'; import { openDialog } from '../../../base/dialog'; import { Icon, IconArrowDown, IconArrowUp } from '../../../base/icons'; +import { participantMatchesSearch } from '../../../participants-pane/functions'; import BreakoutRoomContextMenu from './BreakoutRoomContextMenu'; import BreakoutRoomParticipantItem from './BreakoutRoomParticipantItem'; @@ -17,7 +18,12 @@ type Props = { /** * Room to display. */ - room: Object + room: Object, + + /** + * Participants search string. + */ + searchString: string } /** @@ -31,7 +37,7 @@ function _keyExtractor(item: Object) { } -export const CollapsibleRoom = ({ room }: Props) => { +export const CollapsibleRoom = ({ room, searchString }: Props) => { const dispatch = useDispatch(); const [ collapsed, setCollapsed ] = useState(false); const { t } = useTranslation(); @@ -65,7 +71,8 @@ export const CollapsibleRoom = ({ room }: Props) => { horizontal = { false } keyExtractor = { _keyExtractor } // eslint-disable-next-line react/jsx-no-bind - renderItem = { ({ item: participant }) => } + renderItem = { ({ item: participant }) => participantMatchesSearch(participant, searchString) + && } showsHorizontalScrollIndicator = { false } windowSize = { 2 } />} diff --git a/react/features/breakout-rooms/components/web/CollapsibleRoom.js b/react/features/breakout-rooms/components/web/CollapsibleRoom.js index 08dec64d1..c871f8fe3 100644 --- a/react/features/breakout-rooms/components/web/CollapsibleRoom.js +++ b/react/features/breakout-rooms/components/web/CollapsibleRoom.js @@ -10,6 +10,7 @@ import { ListItem } from '../../../base/components'; import { Icon, IconArrowDown, IconArrowUp } from '../../../base/icons'; import ParticipantItem from '../../../participants-pane/components/web/ParticipantItem'; import { ACTION_TRIGGER } from '../../../participants-pane/constants'; +import { participantMatchesSearch } from '../../../participants-pane/functions'; type Props = { @@ -42,6 +43,11 @@ type Props = { * Room reference. */ room: Object, + + /** + * Participants search string. + */ + searchString: string } const useStyles = makeStyles(theme => { @@ -78,7 +84,8 @@ export const CollapsibleRoom = ({ isHighlighted, onRaiseMenu, onLeave, - room + room, + searchString }: Props) => { const { t } = useTranslation(); const styles = useStyles(); @@ -116,13 +123,14 @@ export const CollapsibleRoom = ({ textChildren = { roomName } trigger = { actionsTrigger } /> {!collapsed && room?.participants - && Object.values(room?.participants || {}).map((p: Object) => ( - - )) + && Object.values(room?.participants || {}).map((p: Object) => + participantMatchesSearch(p, searchString) && ( + + )) } ); diff --git a/react/features/breakout-rooms/components/web/RoomList.js b/react/features/breakout-rooms/components/web/RoomList.js index 01b3ca883..c05e1c6b9 100644 --- a/react/features/breakout-rooms/components/web/RoomList.js +++ b/react/features/breakout-rooms/components/web/RoomList.js @@ -16,7 +16,15 @@ import { LeaveButton } from './LeaveButton'; import RoomActionEllipsis from './RoomActionEllipsis'; import { RoomContextMenu } from './RoomContextMenu'; -export const RoomList = () => { +type Props = { + + /** + * Participants search string. + */ + searchString: string +} + +export const RoomList = ({ searchString }: Props) => { const currentRoomId = useSelector(getCurrentRoomId); const rooms = Object.values(useSelector(getBreakoutRooms, equals)) .filter((room: Object) => room.id !== currentRoomId) @@ -44,7 +52,8 @@ export const RoomList = () => { isHighlighted = { raiseContext.entity === room } onLeave = { lowerMenu } onRaiseMenu = { onRaiseMenu(room) } - room = { room }> + room = { room } + searchString = { searchString }> {!_overflowDrawer && <> {isLocalModerator && !room.isMainRoom diff --git a/react/features/participants-pane/components/native/MeetingParticipantList.js b/react/features/participants-pane/components/native/MeetingParticipantList.js index 5eabac3f5..8353f11e3 100644 --- a/react/features/participants-pane/components/native/MeetingParticipantList.js +++ b/react/features/participants-pane/components/native/MeetingParticipantList.js @@ -8,10 +8,9 @@ import { translate } from '../../../base/i18n'; import { Icon, IconInviteMore } from '../../../base/icons'; import { getLocalParticipant, getParticipantCountWithFake, getRemoteParticipants } from '../../../base/participants'; import { connect } from '../../../base/redux'; -import { normalizeAccents } from '../../../base/util/strings'; import { getBreakoutRooms, getCurrentRoomId } from '../../../breakout-rooms/functions'; import { doInvitePeople } from '../../../invite/actions.native'; -import { shouldRenderInviteButton } from '../../functions'; +import { participantMatchesSearch, shouldRenderInviteButton } from '../../functions'; import ClearableInput from './ClearableInput'; import MeetingParticipantItem from './MeetingParticipantItem'; @@ -55,6 +54,16 @@ type Props = { */ dispatch: Function, + /** + * Participants search string. + */ + searchString: string, + + /** + * Function to update the search string. + */ + setSearchString: Function, + /** * Translation function. */ @@ -66,14 +75,10 @@ type Props = { theme: Object } -type State = { - searchString: string -}; - /** * The meeting participant list component. */ -class MeetingParticipantList extends PureComponent { +class MeetingParticipantList extends PureComponent { /** * Creates new MeetingParticipantList instance. @@ -83,10 +88,6 @@ class MeetingParticipantList extends PureComponent { constructor(props: Props) { super(props); - this.state = { - searchString: '' - }; - this._keyExtractor = this._keyExtractor.bind(this); this._onInvite = this._onInvite.bind(this); this._renderParticipant = this._renderParticipant.bind(this); @@ -139,27 +140,10 @@ class MeetingParticipantList extends PureComponent { * @returns {ReactElement} */ _renderParticipant({ item/* , index, separators */ }) { - const { _localParticipant, _remoteParticipants } = this.props; - const { searchString } = this.state; + const { _localParticipant, _remoteParticipants, searchString } = this.props; const participant = item === _localParticipant?.id ? _localParticipant : _remoteParticipants.get(item); - const displayName = participant?.name || ''; - if (displayName) { - const names = normalizeAccents(displayName) - .toLowerCase() - .split(' '); - const lowerCaseSearch = normalizeAccents(searchString).toLowerCase(); - - for (const name of names) { - if (lowerCaseSearch === '' || name.startsWith(lowerCaseSearch)) { - return ( - - ); - } - } - } else if (displayName === '' && searchString === '') { + if (participantMatchesSearch(participant, searchString)) { return ( { * @returns {void} */ _onSearchStringChange(text: string) { - this.setState({ - searchString: text - }); + this.props.setSearchString(text); } /** diff --git a/react/features/participants-pane/components/native/ParticipantsPane.js b/react/features/participants-pane/components/native/ParticipantsPane.js index f5652e0e5..db0d244b2 100644 --- a/react/features/participants-pane/components/native/ParticipantsPane.js +++ b/react/features/participants-pane/components/native/ParticipantsPane.js @@ -1,6 +1,6 @@ // @flow -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ScrollView, View } from 'react-native'; import { Button } from 'react-native-paper'; @@ -36,6 +36,7 @@ import styles from './styles'; */ const ParticipantsPane = () => { const dispatch = useDispatch(); + const [ searchString, setSearchString ] = useState(''); const openMoreMenu = useCallback(() => dispatch(openDialog(ContextMenuMore)), [ dispatch ]); const isLocalModerator = useSelector(isLocalParticipantModerator); const muteAll = useCallback(() => dispatch(openDialog(MuteEveryoneDialog)), @@ -59,7 +60,9 @@ const ParticipantsPane = () => { style = { styles.participantsPane }> - + {!inBreakoutRoom && isLocalModerator && participantsCount > 2 @@ -69,7 +72,8 @@ const ParticipantsPane = () => { {_isBreakoutRoomsSupported && rooms.map(room => ())} + room = { room } + searchString = { searchString } />))} {_isBreakoutRoomsSupported && !hideAddRoomButton && isLocalModerator && } diff --git a/react/features/participants-pane/components/web/MeetingParticipantItem.js b/react/features/participants-pane/components/web/MeetingParticipantItem.js index 2682f2650..74cdf6c91 100644 --- a/react/features/participants-pane/components/web/MeetingParticipantItem.js +++ b/react/features/participants-pane/components/web/MeetingParticipantItem.js @@ -19,12 +19,12 @@ import { isParticipantAudioMuted, isParticipantVideoMuted } from '../../../base/tracks'; -import { normalizeAccents } from '../../../base/util/strings'; import { ACTION_TRIGGER, type MediaState, MEDIA_STATE } from '../../constants'; import { getParticipantAudioMediaState, getParticipantVideoMediaState, - getQuickActionButtonType + getQuickActionButtonType, + participantMatchesSearch } from '../../functions'; import ParticipantActionEllipsis from './ParticipantActionEllipsis'; @@ -300,22 +300,7 @@ function _mapStateToProps(state, ownProps): Object { const _displayName = getParticipantDisplayName(state, participant?.id); - let _matchesSearch = false; - const names = normalizeAccents(_displayName) - .toLowerCase() - .split(' '); - const lowerCaseSearchString = searchString.toLowerCase(); - - if (lowerCaseSearchString === '') { - _matchesSearch = true; - } else { - for (const name of names) { - if (name.startsWith(lowerCaseSearchString)) { - _matchesSearch = true; - break; - } - } - } + const _matchesSearch = participantMatchesSearch(participant, searchString); const _isAudioMuted = isParticipantAudioMuted(participant, state); const _isVideoMuted = isParticipantVideoMuted(participant, state); diff --git a/react/features/participants-pane/components/web/MeetingParticipants.js b/react/features/participants-pane/components/web/MeetingParticipants.js index 55dcc86fc..06814b619 100644 --- a/react/features/participants-pane/components/web/MeetingParticipants.js +++ b/react/features/participants-pane/components/web/MeetingParticipants.js @@ -1,7 +1,7 @@ // @flow import { makeStyles } from '@material-ui/styles'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; @@ -45,8 +45,10 @@ const useStyles = makeStyles(theme => { type Props = { currentRoom: ?Object, participantsCount: number, - showInviteButton: boolean, overflowDrawer: boolean, + searchString: string, + setSearchString: Function, + showInviteButton: boolean, sortedParticipantIds: Array }; @@ -64,11 +66,12 @@ function MeetingParticipants({ currentRoom, overflowDrawer, participantsCount, + searchString, + setSearchString, showInviteButton, sortedParticipantIds = [] }: Props) { const dispatch = useDispatch(); - const [ searchString, setSearchString ] = useState(''); const { t } = useTranslation(); const [ lowerMenu, , toggleMenu, menuEnter, menuLeave, raiseContext ] = useContextMenu(); diff --git a/react/features/participants-pane/components/web/ParticipantsPane.js b/react/features/participants-pane/components/web/ParticipantsPane.js index 7be8b392b..3b18a7d19 100644 --- a/react/features/participants-pane/components/web/ParticipantsPane.js +++ b/react/features/participants-pane/components/web/ParticipantsPane.js @@ -75,6 +75,11 @@ type State = { * Indicates if the footer context menu is open. */ contextOpen: boolean, + + /** + * Participants search string. + */ + searchString: string }; const styles = theme => { @@ -152,7 +157,8 @@ class ParticipantsPane extends Component { super(props); this.state = { - contextOpen: false + contextOpen: false, + searchString: '' }; // Bind event handlers so they are only bound once per instance. @@ -162,6 +168,7 @@ class ParticipantsPane extends Component { this._onMuteAll = this._onMuteAll.bind(this); this._onToggleContext = this._onToggleContext.bind(this); this._onWindowClickListener = this._onWindowClickListener.bind(this); + this.setSearchString = this.setSearchString.bind(this); } @@ -197,7 +204,7 @@ class ParticipantsPane extends Component { classes, t } = this.props; - const { contextOpen } = this.state; + const { contextOpen, searchString } = this.state; // when the pane is not open optimize to not // execute the MeetingParticipantList render for large list of participants @@ -224,8 +231,10 @@ class ParticipantsPane extends Component {

- - {_isBreakoutRoomsSupported && } + + {_isBreakoutRoomsSupported && } {_showAddRoomButton && }
{_showFooter && ( @@ -255,6 +264,20 @@ class ParticipantsPane extends Component { ); } + setSearchString: (string) => void; + + /** + * Sets the search string. + * + * @param {string} newSearchString - The new search string. + * @returns {void} + */ + setSearchString(newSearchString) { + this.setState({ + searchString: newSearchString + }); + } + _onClosePane: () => void; /** diff --git a/react/features/participants-pane/functions.js b/react/features/participants-pane/functions.js index aafe7729a..2f0f8a942 100644 --- a/react/features/participants-pane/functions.js +++ b/react/features/participants-pane/functions.js @@ -17,6 +17,7 @@ import { getRaiseHandsQueue } from '../base/participants/functions'; import { toState } from '../base/redux'; +import { normalizeAccents } from '../base/util/strings'; import { isInBreakoutRoom } from '../breakout-rooms/functions'; import { QUICK_ACTION_BUTTON, REDUCER_KEY, MEDIA_STATE } from './constants'; @@ -242,3 +243,28 @@ export function getSortedParticipantIds(stateful: Object | Function): Array