feat(native-participants-pane) context menu for meeting participant
This commit is contained in:
parent
47be509d17
commit
0b3991d9e1
|
@ -1,13 +1,24 @@
|
|||
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.
|
||||
*
|
||||
* @param {string} participant - The selected participant's id.
|
||||
* @param {Object} participant - The selected lobby participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
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 });
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ type Props = {
|
|||
participant: Object
|
||||
};
|
||||
|
||||
export const ContextMenuReject = ({ participant: p }: Props) => {
|
||||
export const ContextMenuLobbyParticipantReject = ({ participant: p }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const cancel = useCallback(() => dispatch(hideDialog()), [ dispatch ]);
|
||||
const displayName = p.name;
|
||||
|
@ -35,23 +35,24 @@ export const ContextMenuReject = ({ participant: p }: Props) => {
|
|||
onCancel = { cancel }
|
||||
style = { styles.contextMenuMore }>
|
||||
<View
|
||||
style = { styles.contextMenuItemDetails }>
|
||||
style = { styles.contextMenuItemSection }>
|
||||
<Avatar
|
||||
className = 'participant-avatar'
|
||||
participantId = { p.id }
|
||||
size = { 32 } />
|
||||
<View style = { styles.contextMenuItemText }>
|
||||
<Text style = { styles.contextMenuItemParticipantName }>
|
||||
<Text style = { styles.contextMenuItemName }>
|
||||
{ displayName }
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
onPress = { reject }
|
||||
style = { styles.contextMenuItemReject }>
|
||||
style = { styles.contextMenuItem }>
|
||||
<Icon
|
||||
size = { 24 }
|
||||
src = { IconClose } />
|
||||
src = { IconClose }
|
||||
style = { styles.contextMenuItemIcon } />
|
||||
<Text style = { styles.contextMenuItemText }>{ t('lobby.reject') }</Text>
|
||||
</TouchableOpacity>
|
||||
</BottomSheet>
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -41,14 +41,14 @@ export const ContextMenuMore = ({ exclude }: Props) => {
|
|||
style = { styles.contextMenuMore }>
|
||||
<TouchableOpacity
|
||||
onPress = { muteEveryoneVideo }
|
||||
style = { styles.contextMenuItemMuteVideo }>
|
||||
style = { styles.contextMenuItem }>
|
||||
<Icon
|
||||
size = { 24 }
|
||||
src = { IconVideoOff } />
|
||||
<Text style = { styles.contextMenuItemText }>{t('participantsPane.actions.stopEveryonesVideo')}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style = { styles.contextMenuItemDontAllowUnmute }>
|
||||
style = { styles.contextMenuItem }>
|
||||
<Icon
|
||||
size = { 24 }
|
||||
src = { IconMicDisabledHollow }
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
|
||||
import {
|
||||
getIsParticipantAudioMuted,
|
||||
getIsParticipantVideoMuted
|
||||
} from '../../../base/tracks';
|
||||
import { showContextMenuDetails } from '../../actions.native';
|
||||
import { MediaState } from '../../constants';
|
||||
|
||||
import ParticipantItem from './ParticipantItem';
|
||||
|
@ -21,13 +22,16 @@ type Props = {
|
|||
};
|
||||
|
||||
export const MeetingParticipantItem = ({ participant: p }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const isAudioMuted = useSelector(getIsParticipantAudioMuted(p));
|
||||
const isVideoMuted = useSelector(getIsParticipantVideoMuted(p));
|
||||
const openContextMenuDetails = useCallback(() => dispatch(showContextMenuDetails(p), [ dispatch ]));
|
||||
|
||||
return (
|
||||
<ParticipantItem
|
||||
audioMuteState = { isAudioMuted ? MediaState.Muted : MediaState.Unmuted }
|
||||
name = { p.name }
|
||||
onPress = { openContextMenuDetails }
|
||||
participant = { p }
|
||||
videoMuteState = { isVideoMuted ? MediaState.Muted : MediaState.Unmuted } />
|
||||
);
|
||||
|
|
|
@ -2,3 +2,5 @@
|
|||
|
||||
export { default as ParticipantsPane } from './ParticipantsPane';
|
||||
export { default as ParticipantsPaneButton } from './ParticipantsPaneButton';
|
||||
export { ContextMenuLobbyParticipantReject } from './ContextMenuLobbyParticipantReject';
|
||||
export { ContextMenuMeetingParticipantDetails } from './ContextMenuMeetingParticipantDetails';
|
||||
|
|
|
@ -287,33 +287,29 @@ export default {
|
|||
textTransform: 'capitalize'
|
||||
},
|
||||
|
||||
contextMenuItemMuteVideo: {
|
||||
contextMenuItem: {
|
||||
...contextMenuItem
|
||||
},
|
||||
|
||||
contextMenuItemDontAllowUnmute: {
|
||||
...contextMenuItem
|
||||
},
|
||||
|
||||
contextMenuItemDetails: {
|
||||
contextMenuItemSection: {
|
||||
...contextMenuItem,
|
||||
borderBottomColor: BaseTheme.palette.section01,
|
||||
borderBottomWidth: 1
|
||||
},
|
||||
|
||||
contextMenuItemReject: {
|
||||
...contextMenuItem
|
||||
},
|
||||
|
||||
contextMenuItemText: {
|
||||
...BaseTheme.typography.bodyShortRegularLarge,
|
||||
alignSelf: 'center',
|
||||
color: BaseTheme.palette.text01,
|
||||
flexDirection: 'row',
|
||||
marginLeft: 8
|
||||
marginLeft: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
contextMenuItemParticipantName: {
|
||||
contextMenuItemIcon: {
|
||||
marginLeft: BaseTheme.spacing[1]
|
||||
},
|
||||
|
||||
contextMenuItemName: {
|
||||
...BaseTheme.typography.bodyShortRegularLarge,
|
||||
color: BaseTheme.palette.text01
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue