feat(native-participants-pane) context menu for meeting participant

This commit is contained in:
Calin Chitu 2021-06-04 18:07:18 +03:00 committed by Hristo Terezov
parent 47be509d17
commit 0b3991d9e1
8 changed files with 143 additions and 24 deletions

View File

@ -1,13 +1,24 @@
import { openDialog } from '../base/dialog'; import { openDialog } from '../base/dialog';
import { ContextMenuReject } from './components/native/ContextMenuReject'; import { ContextMenuLobbyParticipantReject, ContextMenuMeetingParticipantDetails } from './components/native';
/** /**
* Displays the context menu for the selected lobby participant. * Displays the context menu for the selected lobby participant.
* *
* @param {string} participant - The selected participant's id. * @param {Object} participant - The selected lobby participant.
* @returns {Function} * @returns {Function}
*/ */
export function showContextMenuReject(participant) { export function showContextMenuReject(participant) {
return openDialog(ContextMenuReject, { participant }); return openDialog(ContextMenuLobbyParticipantReject, { participant });
}
/**
* Displays the context menu for the selected meeting participant.
*
* @param {Object} participant - The selected meeting participant.
* @returns {Function}
*/
export function showContextMenuDetails(participant) {
return openDialog(ContextMenuMeetingParticipantDetails, { participant });
} }

View File

@ -23,7 +23,7 @@ type Props = {
participant: Object participant: Object
}; };
export const ContextMenuReject = ({ participant: p }: Props) => { export const ContextMenuLobbyParticipantReject = ({ participant: p }: Props) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const cancel = useCallback(() => dispatch(hideDialog()), [ dispatch ]); const cancel = useCallback(() => dispatch(hideDialog()), [ dispatch ]);
const displayName = p.name; const displayName = p.name;
@ -35,23 +35,24 @@ export const ContextMenuReject = ({ participant: p }: Props) => {
onCancel = { cancel } onCancel = { cancel }
style = { styles.contextMenuMore }> style = { styles.contextMenuMore }>
<View <View
style = { styles.contextMenuItemDetails }> style = { styles.contextMenuItemSection }>
<Avatar <Avatar
className = 'participant-avatar' className = 'participant-avatar'
participantId = { p.id } participantId = { p.id }
size = { 32 } /> size = { 32 } />
<View style = { styles.contextMenuItemText }> <View style = { styles.contextMenuItemText }>
<Text style = { styles.contextMenuItemParticipantName }> <Text style = { styles.contextMenuItemName }>
{ displayName } { displayName }
</Text> </Text>
</View> </View>
</View> </View>
<TouchableOpacity <TouchableOpacity
onPress = { reject } onPress = { reject }
style = { styles.contextMenuItemReject }> style = { styles.contextMenuItem }>
<Icon <Icon
size = { 24 } size = { 24 }
src = { IconClose } /> src = { IconClose }
style = { styles.contextMenuItemIcon } />
<Text style = { styles.contextMenuItemText }>{ t('lobby.reject') }</Text> <Text style = { styles.contextMenuItemText }>{ t('lobby.reject') }</Text>
</TouchableOpacity> </TouchableOpacity>
</BottomSheet> </BottomSheet>

View File

@ -0,0 +1,105 @@
// @flow
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { TouchableOpacity, View } from 'react-native';
import { Text } from 'react-native-paper';
import { useDispatch } from 'react-redux';
import { Avatar } from '../../../base/avatar';
import { hideDialog } from '../../../base/dialog';
import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
import {
Icon, IconCloseCircle, IconConnectionActive, IconMessage,
IconMicrophoneEmptySlash,
IconMuteEveryoneElse, IconVideoOff
} from '../../../base/icons';
import { MEDIA_TYPE } from '../../../base/media';
import { muteRemote } from '../../../video-menu/actions.any';
import styles from './styles';
type Props = {
/**
* Participant reference
*/
participant: Object
};
export const ContextMenuMeetingParticipantDetails = ({ participant: p }: Props) => {
const dispatch = useDispatch();
const cancel = useCallback(() => dispatch(hideDialog()), [ dispatch ]);
const displayName = p.name;
const muteAudio = useCallback(() => dispatch(muteRemote(p.id, MEDIA_TYPE.AUDIO)), [ dispatch ]);
const { t } = useTranslation();
return (
<BottomSheet
onCancel = { cancel }
style = { styles.contextMenuMore }>
<View
style = { styles.contextMenuItemSection }>
<Avatar
className = 'participant-avatar'
participantId = { p.id }
size = { 32 } />
<View style = { styles.contextMenuItemText }>
<Text style = { styles.contextMenuItemName }>
{ displayName }
</Text>
</View>
</View>
<TouchableOpacity
onPress = { muteAudio }
style = { styles.contextMenuItem }>
<Icon
size = { 24 }
src = { IconMicrophoneEmptySlash }
style = { styles.contextMenuItemIcon } />
<Text style = { styles.contextMenuItemText }>{ t('participantsPane.actions.mute') }</Text>
</TouchableOpacity>
<TouchableOpacity
onPress = { muteAudio }
style = { styles.contextMenuItem }>
<Icon
size = { 24 }
src = { IconMuteEveryoneElse }
style = { styles.contextMenuItemIcon } />
<Text style = { styles.contextMenuItemText }>{ t('participantsPane.actions.muteEveryoneElse') }</Text>
</TouchableOpacity>
<TouchableOpacity
style = { styles.contextMenuItemSection }>
<Icon
size = { 24 }
src = { IconVideoOff }
style = { styles.contextMenuItemIcon } />
<Text style = { styles.contextMenuItemText }>{ t('participantsPane.actions.stopVideo') }</Text>
</TouchableOpacity>
<TouchableOpacity
style = { styles.contextMenuItem }>
<Icon
size = { 24 }
src = { IconCloseCircle }
style = { styles.contextMenuItemIcon } />
<Text style = { styles.contextMenuItemText }>{ t('videothumbnail.kick') }</Text>
</TouchableOpacity>
<TouchableOpacity
style = { styles.contextMenuItem }>
<Icon
size = { 24 }
src = { IconMessage }
style = { styles.contextMenuItemIcon } />
<Text style = { styles.contextMenuItemText }>{ t('toolbar.accessibilityLabel.privateMessage') }</Text>
</TouchableOpacity>
<TouchableOpacity
style = { styles.contextMenuItemSection }>
<Icon
size = { 24 }
src = { IconConnectionActive }
style = { styles.contextMenuItemIcon } />
<Text style = { styles.contextMenuItemText }>{ t('participantsPane.actions.networkStats') }</Text>
</TouchableOpacity>
</BottomSheet>
);
};

View File

@ -41,14 +41,14 @@ export const ContextMenuMore = ({ exclude }: Props) => {
style = { styles.contextMenuMore }> style = { styles.contextMenuMore }>
<TouchableOpacity <TouchableOpacity
onPress = { muteEveryoneVideo } onPress = { muteEveryoneVideo }
style = { styles.contextMenuItemMuteVideo }> style = { styles.contextMenuItem }>
<Icon <Icon
size = { 24 } size = { 24 }
src = { IconVideoOff } /> src = { IconVideoOff } />
<Text style = { styles.contextMenuItemText }>{t('participantsPane.actions.stopEveryonesVideo')}</Text> <Text style = { styles.contextMenuItemText }>{t('participantsPane.actions.stopEveryonesVideo')}</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
style = { styles.contextMenuItemDontAllowUnmute }> style = { styles.contextMenuItem }>
<Icon <Icon
size = { 24 } size = { 24 }
src = { IconMicDisabledHollow } src = { IconMicDisabledHollow }

View File

@ -1,12 +1,13 @@
// @flow // @flow
import React from 'react'; import React, { useCallback } from 'react';
import { useSelector } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { import {
getIsParticipantAudioMuted, getIsParticipantAudioMuted,
getIsParticipantVideoMuted getIsParticipantVideoMuted
} from '../../../base/tracks'; } from '../../../base/tracks';
import { showContextMenuDetails } from '../../actions.native';
import { MediaState } from '../../constants'; import { MediaState } from '../../constants';
import ParticipantItem from './ParticipantItem'; import ParticipantItem from './ParticipantItem';
@ -21,13 +22,16 @@ type Props = {
}; };
export const MeetingParticipantItem = ({ participant: p }: Props) => { export const MeetingParticipantItem = ({ participant: p }: Props) => {
const dispatch = useDispatch();
const isAudioMuted = useSelector(getIsParticipantAudioMuted(p)); const isAudioMuted = useSelector(getIsParticipantAudioMuted(p));
const isVideoMuted = useSelector(getIsParticipantVideoMuted(p)); const isVideoMuted = useSelector(getIsParticipantVideoMuted(p));
const openContextMenuDetails = useCallback(() => dispatch(showContextMenuDetails(p), [ dispatch ]));
return ( return (
<ParticipantItem <ParticipantItem
audioMuteState = { isAudioMuted ? MediaState.Muted : MediaState.Unmuted } audioMuteState = { isAudioMuted ? MediaState.Muted : MediaState.Unmuted }
name = { p.name } name = { p.name }
onPress = { openContextMenuDetails }
participant = { p } participant = { p }
videoMuteState = { isVideoMuted ? MediaState.Muted : MediaState.Unmuted } /> videoMuteState = { isVideoMuted ? MediaState.Muted : MediaState.Unmuted } />
); );

View File

@ -2,3 +2,5 @@
export { default as ParticipantsPane } from './ParticipantsPane'; export { default as ParticipantsPane } from './ParticipantsPane';
export { default as ParticipantsPaneButton } from './ParticipantsPaneButton'; export { default as ParticipantsPaneButton } from './ParticipantsPaneButton';
export { ContextMenuLobbyParticipantReject } from './ContextMenuLobbyParticipantReject';
export { ContextMenuMeetingParticipantDetails } from './ContextMenuMeetingParticipantDetails';

View File

@ -287,33 +287,29 @@ export default {
textTransform: 'capitalize' textTransform: 'capitalize'
}, },
contextMenuItemMuteVideo: { contextMenuItem: {
...contextMenuItem ...contextMenuItem
}, },
contextMenuItemDontAllowUnmute: { contextMenuItemSection: {
...contextMenuItem
},
contextMenuItemDetails: {
...contextMenuItem, ...contextMenuItem,
borderBottomColor: BaseTheme.palette.section01, borderBottomColor: BaseTheme.palette.section01,
borderBottomWidth: 1 borderBottomWidth: 1
}, },
contextMenuItemReject: {
...contextMenuItem
},
contextMenuItemText: { contextMenuItemText: {
...BaseTheme.typography.bodyShortRegularLarge, ...BaseTheme.typography.bodyShortRegularLarge,
alignSelf: 'center', alignSelf: 'center',
color: BaseTheme.palette.text01, color: BaseTheme.palette.text01,
flexDirection: 'row', flexDirection: 'row',
marginLeft: 8 marginLeft: BaseTheme.spacing[3]
}, },
contextMenuItemParticipantName: { contextMenuItemIcon: {
marginLeft: BaseTheme.spacing[1]
},
contextMenuItemName: {
...BaseTheme.typography.bodyShortRegularLarge, ...BaseTheme.typography.bodyShortRegularLarge,
color: BaseTheme.palette.text01 color: BaseTheme.palette.text01
}, },