fix(participants) sort participants alphabetically (#9741)
This commit is contained in:
parent
9914a5d14a
commit
595df524b0
|
@ -443,3 +443,45 @@ async function _getFirstLoadableAvatarUrl(participant, store) {
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selector for retrieving sorted participants by display name.
|
||||||
|
*
|
||||||
|
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
|
||||||
|
* {@code getState} function to be used to retrieve the state
|
||||||
|
* features/base/participants.
|
||||||
|
* @returns {Array<Object>}
|
||||||
|
*/
|
||||||
|
export function getSortedParticipants(stateful: Object | Function) {
|
||||||
|
const localParticipant = getLocalParticipant(stateful);
|
||||||
|
const remoteParticipants = getRemoteParticipants(stateful);
|
||||||
|
|
||||||
|
const items = [];
|
||||||
|
|
||||||
|
remoteParticipants.forEach(p => {
|
||||||
|
items.push(p);
|
||||||
|
});
|
||||||
|
|
||||||
|
items.sort((a, b) =>
|
||||||
|
getParticipantDisplayName(stateful, a.id).localeCompare(getParticipantDisplayName(stateful, b.id))
|
||||||
|
);
|
||||||
|
|
||||||
|
items.unshift(localParticipant);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selector for retrieving ids of alphabetically sorted participants by name.
|
||||||
|
*
|
||||||
|
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
|
||||||
|
* {@code getState} function to be used to retrieve the state
|
||||||
|
* features/base/participants.
|
||||||
|
* @returns {Array<string>}
|
||||||
|
*/
|
||||||
|
export function getSortedParticipantIds(stateful: Object | Function): Array<string> {
|
||||||
|
const participantIds = getSortedParticipants(stateful).map((p): Object => p.id);
|
||||||
|
|
||||||
|
return participantIds;
|
||||||
|
}
|
||||||
|
|
|
@ -30,8 +30,6 @@ import { isParticipantModerator } from './functions';
|
||||||
* @property {string} email - Participant email.
|
* @property {string} email - Participant email.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
declare var APP: Object;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The participant properties which cannot be updated through
|
* The participant properties which cannot be updated through
|
||||||
* {@link PARTICIPANT_UPDATED}. They either identify the participant or can only
|
* {@link PARTICIPANT_UPDATED}. They either identify the participant or can only
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { isLocalParticipantModerator } from '../../base/participants';
|
import { isLocalParticipantModerator } from '../../base/participants';
|
||||||
import { setKnockingParticipantApproval } from '../actions';
|
import { setKnockingParticipantApproval } from '../actions';
|
||||||
import { getLobbyState } from '../functions';
|
import { getKnockingParticipants, getLobbyEnabled } from '../functions';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
|
|
||||||
|
@ -67,7 +67,8 @@ export default class AbstractKnockingParticipantList<P: Props = Props> extends P
|
||||||
* @returns {Props}
|
* @returns {Props}
|
||||||
*/
|
*/
|
||||||
export function mapStateToProps(state: Object): $Shape<Props> {
|
export function mapStateToProps(state: Object): $Shape<Props> {
|
||||||
const { knockingParticipants, lobbyEnabled } = getLobbyState(state);
|
const lobbyEnabled = getLobbyEnabled(state);
|
||||||
|
const knockingParticipants = getKnockingParticipants(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_participants: knockingParticipants,
|
_participants: knockingParticipants,
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selector to return lobby state.
|
* Selector to return lobby enable state.
|
||||||
*
|
*
|
||||||
* @param {any} state - State object.
|
* @param {any} state - State object.
|
||||||
* @returns {any}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
export function getLobbyState(state: any) {
|
export function getLobbyEnabled(state: any) {
|
||||||
return state['features/lobby'];
|
return state['features/lobby'].lobbyEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selector to return a list of knocking participants.
|
||||||
|
*
|
||||||
|
* @param {any} state - State object.
|
||||||
|
* @returns {Array<Object>}
|
||||||
|
*/
|
||||||
|
export function getKnockingParticipants(state: any) {
|
||||||
|
return state['features/lobby'].knockingParticipants;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selector to return array with knocking participant ids.
|
* Selector to return array with knocking participant ids.
|
||||||
|
@ -18,7 +27,5 @@ export function getLobbyState(state: any) {
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
export function getKnockingParticipantsById(state: any) {
|
export function getKnockingParticipantsById(state: any) {
|
||||||
const { knockingParticipants } = state['features/lobby'];
|
return getKnockingParticipants(state).map(participant => participant.id);
|
||||||
|
|
||||||
return knockingParticipants.map(participant => participant.id);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Button, withTheme } from 'react-native-paper';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import { admitMultiple } from '../../../lobby/actions.native';
|
import { admitMultiple } from '../../../lobby/actions.native';
|
||||||
import { getLobbyState } from '../../../lobby/functions';
|
import { getKnockingParticipants, getLobbyEnabled } from '../../../lobby/functions';
|
||||||
|
|
||||||
import { LobbyParticipantItem } from './LobbyParticipantItem';
|
import { LobbyParticipantItem } from './LobbyParticipantItem';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
@ -21,10 +21,9 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const LobbyParticipantList = ({ theme }: Props) => {
|
const LobbyParticipantList = ({ theme }: Props) => {
|
||||||
const {
|
const lobbyEnabled = useSelector(getLobbyEnabled);
|
||||||
lobbyEnabled,
|
const participants = useSelector(getKnockingParticipants);
|
||||||
knockingParticipants: participants
|
|
||||||
} = useSelector(getLobbyState);
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const admitAll = useCallback(() =>
|
const admitAll = useCallback(() =>
|
||||||
dispatch(admitMultiple(participants)),
|
dispatch(admitMultiple(participants)),
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { Icon, IconInviteMore } from '../../../base/icons';
|
||||||
import {
|
import {
|
||||||
getLocalParticipant,
|
getLocalParticipant,
|
||||||
getParticipantCountWithFake,
|
getParticipantCountWithFake,
|
||||||
getRemoteParticipants
|
getSortedParticipants
|
||||||
} from '../../../base/participants';
|
} from '../../../base/participants';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
import { doInvitePeople } from '../../../invite/actions.native';
|
import { doInvitePeople } from '../../../invite/actions.native';
|
||||||
|
@ -35,11 +35,9 @@ type Props = {
|
||||||
|
|
||||||
const MeetingParticipantList = ({ _localVideoOwner }: Props) => {
|
const MeetingParticipantList = ({ _localVideoOwner }: Props) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const items = [];
|
|
||||||
const localParticipant = useSelector(getLocalParticipant);
|
|
||||||
const onInvite = useCallback(() => dispatch(doInvitePeople()), [ dispatch ]);
|
const onInvite = useCallback(() => dispatch(doInvitePeople()), [ dispatch ]);
|
||||||
const participants = useSelector(getRemoteParticipants);
|
|
||||||
const participantsCount = useSelector(getParticipantCountWithFake);
|
const participantsCount = useSelector(getParticipantCountWithFake);
|
||||||
|
const sortedParticipants = useSelector(getSortedParticipants);
|
||||||
const showInviteButton = useSelector(shouldRenderInviteButton);
|
const showInviteButton = useSelector(shouldRenderInviteButton);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
@ -73,12 +71,6 @@ const MeetingParticipantList = ({ _localVideoOwner }: Props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
items.push(renderParticipant(localParticipant));
|
|
||||||
|
|
||||||
participants.forEach(p => {
|
|
||||||
items.push(renderParticipant(p));
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style = { styles.meetingList }>
|
<View style = { styles.meetingList }>
|
||||||
<Text style = { styles.meetingListDescription }>
|
<Text style = { styles.meetingListDescription }>
|
||||||
|
@ -100,7 +92,7 @@ const MeetingParticipantList = ({ _localVideoOwner }: Props) => {
|
||||||
onPress = { onInvite }
|
onPress = { onInvite }
|
||||||
style = { styles.inviteButton } />
|
style = { styles.inviteButton } />
|
||||||
}
|
}
|
||||||
{ items }
|
{ sortedParticipants.map(renderParticipant) }
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
|
||||||
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
||||||
import { admitMultiple } from '../../../lobby/actions.web';
|
import { admitMultiple } from '../../../lobby/actions.web';
|
||||||
import { getLobbyState } from '../../../lobby/functions';
|
import { getKnockingParticipants, getLobbyEnabled } from '../../../lobby/functions';
|
||||||
|
|
||||||
import { LobbyParticipantItem } from './LobbyParticipantItem';
|
import { LobbyParticipantItem } from './LobbyParticipantItem';
|
||||||
|
|
||||||
|
@ -32,10 +32,9 @@ const useStyles = makeStyles(theme => {
|
||||||
|
|
||||||
|
|
||||||
export const LobbyParticipantList = () => {
|
export const LobbyParticipantList = () => {
|
||||||
const {
|
const lobbyEnabled = useSelector(getLobbyEnabled);
|
||||||
lobbyEnabled,
|
const participants = useSelector(getKnockingParticipants);
|
||||||
knockingParticipants: participants
|
|
||||||
} = useSelector(getLobbyState);
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
|
@ -6,9 +6,8 @@ import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
|
||||||
import { openDialog } from '../../../base/dialog';
|
import { openDialog } from '../../../base/dialog';
|
||||||
import {
|
import {
|
||||||
getLocalParticipant,
|
|
||||||
getParticipantCountWithFake,
|
getParticipantCountWithFake,
|
||||||
getRemoteParticipants
|
getSortedParticipantIds
|
||||||
} from '../../../base/participants';
|
} from '../../../base/participants';
|
||||||
import MuteRemoteParticipantDialog from '../../../video-menu/components/web/MuteRemoteParticipantDialog';
|
import MuteRemoteParticipantDialog from '../../../video-menu/components/web/MuteRemoteParticipantDialog';
|
||||||
import { findStyledAncestor, shouldRenderInviteButton } from '../../functions';
|
import { findStyledAncestor, shouldRenderInviteButton } from '../../functions';
|
||||||
|
@ -46,8 +45,7 @@ const initialState = Object.freeze(Object.create(null));
|
||||||
export function MeetingParticipantList() {
|
export function MeetingParticipantList() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const isMouseOverMenu = useRef(false);
|
const isMouseOverMenu = useRef(false);
|
||||||
const participants = useSelector(getRemoteParticipants);
|
const sortedParticipantIds = useSelector(getSortedParticipantIds);
|
||||||
const localParticipant = useSelector(getLocalParticipant);
|
|
||||||
|
|
||||||
// This is very important as getRemoteParticipants is not changing its reference object
|
// This is very important as getRemoteParticipants is not changing its reference object
|
||||||
// and we will not re-render on change, but if count changes we will do
|
// and we will not re-render on change, but if count changes we will do
|
||||||
|
@ -130,19 +128,12 @@ export function MeetingParticipantList() {
|
||||||
youText = { youText } />
|
youText = { youText } />
|
||||||
);
|
);
|
||||||
|
|
||||||
const items = [];
|
|
||||||
|
|
||||||
localParticipant && items.push(renderParticipant(localParticipant?.id));
|
|
||||||
participants.forEach(p => {
|
|
||||||
items.push(renderParticipant(p?.id));
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Heading>{t('participantsPane.headings.participantsList', { count: participantsCount })}</Heading>
|
<Heading>{t('participantsPane.headings.participantsList', { count: participantsCount })}</Heading>
|
||||||
{showInviteButton && <InviteButton />}
|
{showInviteButton && <InviteButton />}
|
||||||
<div>
|
<div>
|
||||||
{ items }
|
{sortedParticipantIds.map(renderParticipant)}
|
||||||
</div>
|
</div>
|
||||||
<MeetingParticipantContextMenu
|
<MeetingParticipantContextMenu
|
||||||
muteAudio = { muteAudio }
|
muteAudio = { muteAudio }
|
||||||
|
|
Loading…
Reference in New Issue