fix(rn,ParticipantPane) optimize
Use a FlatList to avoid loading all participants at once.
This commit is contained in:
parent
221cf67d0a
commit
dfd53f4041
|
@ -29,28 +29,28 @@ export function showContextMenuReject(participant: Object) {
|
|||
* @param {string} participantID - The selected meeting participant id.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showConnectionStatus(participantID: String) {
|
||||
export function showConnectionStatus(participantID: string) {
|
||||
return openDialog(ConnectionStatusComponent, { participantID });
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the context menu for the selected meeting participant.
|
||||
*
|
||||
* @param {Object} participant - The selected meeting participant.
|
||||
* @param {string} participantId - The ID of the selected meeting participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showContextMenuDetails(participant: Object) {
|
||||
return openDialog(RemoteVideoMenu, { participant });
|
||||
export function showContextMenuDetails(participantId: string) {
|
||||
return openDialog(RemoteVideoMenu, { participantId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the shared video menu.
|
||||
*
|
||||
* @param {Object} participant - The selected meeting participant.
|
||||
* @param {string} participantId - The ID of the selected meeting participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showSharedVideoMenu(participant: Object) {
|
||||
return openDialog(SharedVideoMenu, { participant });
|
||||
export function showSharedVideoMenu(participantId: string) {
|
||||
return openDialog(SharedVideoMenu, { participantId });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Text, View } from 'react-native';
|
||||
import { ScrollView, Text, View } from 'react-native';
|
||||
import { Button, withTheme } from 'react-native-paper';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
|
@ -54,13 +54,15 @@ const LobbyParticipantList = ({ theme }: Props) => {
|
|||
)
|
||||
}
|
||||
</View>
|
||||
{
|
||||
participants.map(p => (
|
||||
<LobbyParticipantItem
|
||||
key = { p.id }
|
||||
participant = { p } />)
|
||||
)
|
||||
}
|
||||
<ScrollView>
|
||||
{
|
||||
participants.map(p => (
|
||||
<LobbyParticipantItem
|
||||
key = { p.id }
|
||||
participant = { p } />)
|
||||
)
|
||||
}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantByIdOrUndefined,
|
||||
getParticipantDisplayName
|
||||
} from '../../../base/participants';
|
||||
|
@ -12,6 +13,7 @@ import {
|
|||
isParticipantAudioMuted,
|
||||
isParticipantVideoMuted
|
||||
} from '../../../base/tracks';
|
||||
import { showConnectionStatus, showContextMenuDetails, showSharedVideoMenu } from '../../actions.native';
|
||||
import { MEDIA_STATE } from '../../constants';
|
||||
import type { MediaState } from '../../constants';
|
||||
import { getParticipantAudioMediaState } from '../../functions';
|
||||
|
@ -31,6 +33,11 @@ type Props = {
|
|||
*/
|
||||
_displayName: string,
|
||||
|
||||
/**
|
||||
* True if the participant is fake.
|
||||
*/
|
||||
_isFakeParticipant: boolean,
|
||||
|
||||
/**
|
||||
* True if the participant is video muted.
|
||||
*/
|
||||
|
@ -41,6 +48,11 @@ type Props = {
|
|||
*/
|
||||
_local: boolean,
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
*/
|
||||
_localVideoOwner: boolean,
|
||||
|
||||
/**
|
||||
* The participant ID.
|
||||
*/
|
||||
|
@ -52,9 +64,9 @@ type Props = {
|
|||
_raisedHand: boolean,
|
||||
|
||||
/**
|
||||
* Callback to invoke when item is pressed.
|
||||
* The redux dispatch function.
|
||||
*/
|
||||
onPress: Function,
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* The ID of the participant.
|
||||
|
@ -64,30 +76,75 @@ type Props = {
|
|||
|
||||
/**
|
||||
* Implements the MeetingParticipantItem component.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function MeetingParticipantItem({
|
||||
_audioMediaState,
|
||||
_displayName,
|
||||
_isVideoMuted,
|
||||
_local,
|
||||
_participantID,
|
||||
_raisedHand,
|
||||
onPress
|
||||
}: Props) {
|
||||
return (
|
||||
<ParticipantItem
|
||||
audioMediaState = { _audioMediaState }
|
||||
displayName = { _displayName }
|
||||
isKnockingParticipant = { false }
|
||||
local = { _local }
|
||||
onPress = { onPress }
|
||||
participantID = { _participantID }
|
||||
raisedHand = { _raisedHand }
|
||||
videoMediaState = { _isVideoMuted ? MEDIA_STATE.MUTED : MEDIA_STATE.UNMUTED } />
|
||||
);
|
||||
class MeetingParticipantItem extends PureComponent<Props> {
|
||||
|
||||
/**
|
||||
* Creates new MeetingParticipantItem instance.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._onPress = this._onPress.bind(this);
|
||||
}
|
||||
|
||||
_onPress: () => void;
|
||||
|
||||
/**
|
||||
* Handles MeetingParticipantItem press events.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onPress() {
|
||||
const {
|
||||
_local,
|
||||
_localVideoOwner,
|
||||
_isFakeParticipant,
|
||||
_participantID,
|
||||
dispatch
|
||||
} = this.props;
|
||||
|
||||
if (_isFakeParticipant && _localVideoOwner) {
|
||||
dispatch(showSharedVideoMenu(_participantID));
|
||||
} else if (!_isFakeParticipant) {
|
||||
if (_local) {
|
||||
dispatch(showConnectionStatus(_participantID));
|
||||
} else {
|
||||
dispatch(showContextMenuDetails(_participantID));
|
||||
}
|
||||
} // else no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
_audioMediaState,
|
||||
_displayName,
|
||||
_isVideoMuted,
|
||||
_local,
|
||||
_participantID,
|
||||
_raisedHand
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ParticipantItem
|
||||
audioMediaState = { _audioMediaState }
|
||||
displayName = { _displayName }
|
||||
isKnockingParticipant = { false }
|
||||
local = { _local }
|
||||
onPress = { this._onPress }
|
||||
participantID = { _participantID }
|
||||
raisedHand = { _raisedHand }
|
||||
videoMediaState = { _isVideoMuted ? MEDIA_STATE.MUTED : MEDIA_STATE.UNMUTED } />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,19 +157,21 @@ function MeetingParticipantItem({
|
|||
*/
|
||||
function mapStateToProps(state, ownProps): Object {
|
||||
const { participantID } = ownProps;
|
||||
const { ownerId } = state['features/shared-video'];
|
||||
const localParticipantId = getLocalParticipant(state).id;
|
||||
const participant = getParticipantByIdOrUndefined(state, participantID);
|
||||
const _isAudioMuted = isParticipantAudioMuted(participant, state);
|
||||
const isVideoMuted = isParticipantVideoMuted(participant, state);
|
||||
const audioMediaState = getParticipantAudioMediaState(
|
||||
participant, _isAudioMuted, state
|
||||
);
|
||||
const audioMediaState = getParticipantAudioMediaState(participant, _isAudioMuted, state);
|
||||
|
||||
return {
|
||||
_audioMediaState: audioMediaState,
|
||||
_displayName: getParticipantDisplayName(state, participant?.id),
|
||||
_isAudioMuted,
|
||||
_isFakeParticipant: Boolean(participant?.isFakeParticipant),
|
||||
_isVideoMuted: isVideoMuted,
|
||||
_local: Boolean(participant?.local),
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId),
|
||||
_participantID: participant?.id,
|
||||
_raisedHand: Boolean(participant?.raisedHand)
|
||||
};
|
||||
|
|
|
@ -1,25 +1,14 @@
|
|||
// @flow
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Text, View } from 'react-native';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { FlatList, Text, View } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconInviteMore } from '../../../base/icons';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantCountWithFake,
|
||||
getSortedParticipants
|
||||
} from '../../../base/participants';
|
||||
import { getLocalParticipant, getParticipantCountWithFake } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { doInvitePeople } from '../../../invite/actions.native';
|
||||
import {
|
||||
showConnectionStatus,
|
||||
showContextMenuDetails,
|
||||
showSharedVideoMenu
|
||||
} from '../../actions.native';
|
||||
import { shouldRenderInviteButton } from '../../functions';
|
||||
|
||||
import MeetingParticipantItem from './MeetingParticipantItem';
|
||||
|
@ -28,74 +17,150 @@ import styles from './styles';
|
|||
type Props = {
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
* The ID of the local participant.
|
||||
*/
|
||||
_localVideoOwner: boolean
|
||||
_localParticipantId: string,
|
||||
|
||||
/**
|
||||
* The number of participants in the conference.
|
||||
*/
|
||||
_participantsCount: number,
|
||||
|
||||
/**
|
||||
* Whether or not to show the invite button.
|
||||
*/
|
||||
_showInviteButton: boolean,
|
||||
|
||||
/**
|
||||
* The remote participants.
|
||||
*/
|
||||
_sortedRemoteParticipants: Map<string, string>,
|
||||
|
||||
/**
|
||||
* The redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Translation function.
|
||||
*/
|
||||
t: Function
|
||||
}
|
||||
|
||||
const MeetingParticipantList = ({ _localVideoOwner }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const onInvite = useCallback(() => dispatch(doInvitePeople()), [ dispatch ]);
|
||||
const participantsCount = useSelector(getParticipantCountWithFake);
|
||||
const sortedParticipants = useSelector(getSortedParticipants);
|
||||
const showInviteButton = useSelector(shouldRenderInviteButton);
|
||||
const { t } = useTranslation();
|
||||
/**
|
||||
* The meeting participant list component.
|
||||
*/
|
||||
class MeetingParticipantList extends PureComponent<Props> {
|
||||
|
||||
// eslint-disable-next-line react/no-multi-comp
|
||||
const renderParticipant = p => {
|
||||
if (p.isFakeParticipant) {
|
||||
if (_localVideoOwner) {
|
||||
return (
|
||||
<MeetingParticipantItem
|
||||
key = { p.id }
|
||||
/* eslint-disable-next-line react/jsx-no-bind,no-confusing-arrow */
|
||||
onPress = { () => dispatch(showSharedVideoMenu(p)) }
|
||||
participantID = { p.id } />
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Creates new MeetingParticipantList instance.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
return (
|
||||
<MeetingParticipantItem
|
||||
key = { p.id }
|
||||
participantID = { p.id } />
|
||||
);
|
||||
}
|
||||
this._keyExtractor = this._keyExtractor.bind(this);
|
||||
this._onInvite = this._onInvite.bind(this);
|
||||
this._renderParticipant = this._renderParticipant.bind(this);
|
||||
}
|
||||
|
||||
_keyExtractor: Function;
|
||||
|
||||
/**
|
||||
* Returns a key for a passed item of the list.
|
||||
*
|
||||
* @param {string} item - The user ID.
|
||||
* @returns {string} - The user ID.
|
||||
*/
|
||||
_keyExtractor(item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
_onInvite: () => void;
|
||||
|
||||
/**
|
||||
* Handles ivite button presses.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onInvite() {
|
||||
this.props.dispatch(doInvitePeople());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the "invite more" icon.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderInviteMoreIcon() {
|
||||
return (
|
||||
<Icon
|
||||
size = { 20 }
|
||||
src = { IconInviteMore } />
|
||||
);
|
||||
}
|
||||
|
||||
_renderParticipant: Object => Object;
|
||||
|
||||
/**
|
||||
* Renders a participant.
|
||||
*
|
||||
* @param {Object} flatListItem - Information about the item to be rendered.
|
||||
* @param {string} flatListItem.item - The ID of the participant.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderParticipant({ item/* , index, separators */ }) {
|
||||
return (
|
||||
<MeetingParticipantItem
|
||||
key = { p.id }
|
||||
/* eslint-disable-next-line react/jsx-no-bind,no-confusing-arrow */
|
||||
onPress = { () => p.local
|
||||
? dispatch(showConnectionStatus(p.id)) : dispatch(showContextMenuDetails(p)) }
|
||||
participantID = { p.id } />
|
||||
key = { item }
|
||||
participantID = { item } />
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { styles.meetingList }>
|
||||
<Text style = { styles.meetingListDescription }>
|
||||
{t('participantsPane.headings.participantsList',
|
||||
{ count: participantsCount })}
|
||||
</Text>
|
||||
{
|
||||
showInviteButton
|
||||
&& <Button
|
||||
children = { t('participantsPane.actions.invite') }
|
||||
/* eslint-disable-next-line react/jsx-no-bind */
|
||||
icon = { () =>
|
||||
(<Icon
|
||||
size = { 20 }
|
||||
src = { IconInviteMore } />)
|
||||
}
|
||||
labelStyle = { styles.inviteLabel }
|
||||
mode = 'contained'
|
||||
onPress = { onInvite }
|
||||
style = { styles.inviteButton } />
|
||||
}
|
||||
{ sortedParticipants.map(renderParticipant) }
|
||||
</View>
|
||||
);
|
||||
};
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
_localParticipantId,
|
||||
_participantsCount,
|
||||
_showInviteButton,
|
||||
_sortedRemoteParticipants,
|
||||
t
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<View style = { styles.meetingList }>
|
||||
<Text style = { styles.meetingListDescription }>
|
||||
{t('participantsPane.headings.participantsList',
|
||||
{ count: _participantsCount })}
|
||||
</Text>
|
||||
{
|
||||
_showInviteButton
|
||||
&& <Button
|
||||
children = { t('participantsPane.actions.invite') }
|
||||
icon = { this._renderInviteMoreIcon }
|
||||
labelStyle = { styles.inviteLabel }
|
||||
mode = 'contained'
|
||||
onPress = { this._onInvite }
|
||||
style = { styles.inviteButton } />
|
||||
}
|
||||
<FlatList
|
||||
bounces = { false }
|
||||
data = { [ _localParticipantId, ..._sortedRemoteParticipants ] }
|
||||
horizontal = { false }
|
||||
keyExtractor = { this._keyExtractor }
|
||||
renderItem = { this._renderParticipant }
|
||||
showsHorizontalScrollIndicator = { false }
|
||||
windowSize = { 2 } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the associated props for this component.
|
||||
|
@ -105,12 +170,16 @@ const MeetingParticipantList = ({ _localVideoOwner }: Props) => {
|
|||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
const { ownerId } = state['features/shared-video'];
|
||||
const localParticipantId = getLocalParticipant(state).id;
|
||||
const _participantsCount = getParticipantCountWithFake(state);
|
||||
const { remoteParticipants } = state['features/filmstrip'];
|
||||
const _showInviteButton = shouldRenderInviteButton(state);
|
||||
|
||||
return {
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId)
|
||||
_participantsCount,
|
||||
_showInviteButton,
|
||||
_sortedRemoteParticipants: remoteParticipants,
|
||||
_localParticipantId: getLocalParticipant(state)?.id
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(MeetingParticipantList);
|
||||
export default translate(connect(_mapStateToProps)(MeetingParticipantList));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ScrollView, View } from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
|
@ -45,10 +45,8 @@ const ParticipantsPane = () => {
|
|||
}}
|
||||
onClose = { closePane }
|
||||
style = { styles.participantsPane }>
|
||||
<ScrollView>
|
||||
<LobbyParticipantList />
|
||||
<MeetingParticipantList />
|
||||
</ScrollView>
|
||||
<LobbyParticipantList />
|
||||
<MeetingParticipantList />
|
||||
{
|
||||
isLocalModerator
|
||||
&& <View style = { styles.footer }>
|
||||
|
|
Loading…
Reference in New Issue