fix(av-moderation): buttons for participants pane (#12977)
* fix(av): buttons for participants pane * fix tests * fix lint * rename cliked from participant pane
This commit is contained in:
parent
f727b9295f
commit
02c232440e
|
@ -15,11 +15,15 @@ export type MediaType = 'audio' | 'video' | 'screenshare';
|
|||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
export const MEDIA_TYPE: { [key: string]: MediaType; } = {
|
||||
AUDIO: 'audio',
|
||||
SCREENSHARE: 'screenshare',
|
||||
VIDEO: 'video'
|
||||
};
|
||||
export const MEDIA_TYPE: {
|
||||
AUDIO: MediaType;
|
||||
SCREENSHARE: MediaType;
|
||||
VIDEO: MediaType;
|
||||
} = {
|
||||
AUDIO: 'audio',
|
||||
SCREENSHARE: 'screenshare',
|
||||
VIDEO: 'video'
|
||||
};
|
||||
|
||||
|
||||
/* eslint-disable no-bitwise */
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet';
|
||||
import { MEDIA_TYPE } from '../../../base/media';
|
||||
import {
|
||||
|
@ -163,9 +162,9 @@ type Props = {
|
|||
participantID: ?string,
|
||||
|
||||
/**
|
||||
* The translate function.
|
||||
*/
|
||||
t: Function,
|
||||
* Callback used to stop a participant's video.
|
||||
*/
|
||||
stopVideo: Function,
|
||||
|
||||
/**
|
||||
* The translated "you" text.
|
||||
|
@ -192,17 +191,15 @@ function MeetingParticipantItem({
|
|||
_quickActionButtonType,
|
||||
_raisedHand,
|
||||
_videoMediaState,
|
||||
askUnmuteText,
|
||||
isHighlighted,
|
||||
isInBreakoutRoom,
|
||||
muteAudio,
|
||||
muteParticipantButtonText,
|
||||
onContextMenu,
|
||||
onLeave,
|
||||
openDrawerForParticipant,
|
||||
overflowDrawer,
|
||||
participantActionEllipsisLabel,
|
||||
t,
|
||||
stopVideo,
|
||||
youText
|
||||
}: Props) {
|
||||
|
||||
|
@ -242,12 +239,6 @@ function MeetingParticipantItem({
|
|||
const audioMediaState = _audioMediaState === MEDIA_STATE.UNMUTED && hasAudioLevels
|
||||
? MEDIA_STATE.DOMINANT_SPEAKER : _audioMediaState;
|
||||
|
||||
let askToUnmuteText = askUnmuteText;
|
||||
|
||||
if (_audioMediaState !== MEDIA_STATE.FORCE_MUTED && _videoMediaState === MEDIA_STATE.FORCE_MUTED) {
|
||||
askToUnmuteText = t('participantsPane.actions.allowVideo');
|
||||
}
|
||||
|
||||
return (
|
||||
<ParticipantItem
|
||||
actionsTrigger = { ACTION_TRIGGER.HOVER }
|
||||
|
@ -273,16 +264,16 @@ function MeetingParticipantItem({
|
|||
&& <>
|
||||
{!isInBreakoutRoom && (
|
||||
<ParticipantQuickAction
|
||||
askUnmuteText = { askToUnmuteText }
|
||||
buttonType = { _quickActionButtonType }
|
||||
muteAudio = { muteAudio }
|
||||
muteParticipantButtonText = { muteParticipantButtonText }
|
||||
participantID = { _participantID }
|
||||
participantName = { _displayName } />
|
||||
participantName = { _displayName }
|
||||
stopVideo = { stopVideo } />
|
||||
)}
|
||||
<ParticipantActionEllipsis
|
||||
accessibilityLabel = { participantActionEllipsisLabel }
|
||||
onClick = { onContextMenu } />
|
||||
onClick = { onContextMenu }
|
||||
participantID = { _participantID } />
|
||||
</>
|
||||
}
|
||||
|
||||
|
@ -318,7 +309,7 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
const _isVideoMuted = isParticipantVideoMuted(participant, state);
|
||||
const _audioMediaState = getParticipantAudioMediaState(participant, _isAudioMuted, state);
|
||||
const _videoMediaState = getParticipantVideoMediaState(participant, _isVideoMuted, state);
|
||||
const _quickActionButtonType = getQuickActionButtonType(participant, _isAudioMuted, state);
|
||||
const _quickActionButtonType = getQuickActionButtonType(participant, _isAudioMuted, _isVideoMuted, state);
|
||||
|
||||
const tracks = state['features/base/tracks'];
|
||||
const _audioTrack = participantID === localParticipantId
|
||||
|
@ -342,4 +333,4 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(MeetingParticipantItem));
|
||||
export default connect(_mapStateToProps)(MeetingParticipantItem);
|
||||
|
|
|
@ -66,6 +66,11 @@ type Props = {
|
|||
*/
|
||||
searchString?: string,
|
||||
|
||||
/**
|
||||
* Callback used to stop a participant's video.
|
||||
*/
|
||||
stopVideo: Function,
|
||||
|
||||
/**
|
||||
* The translated "you" text.
|
||||
*/
|
||||
|
@ -78,28 +83,25 @@ type Props = {
|
|||
* @returns {ReactNode}
|
||||
*/
|
||||
function MeetingParticipantItems({
|
||||
askUnmuteText,
|
||||
isInBreakoutRoom,
|
||||
lowerMenu,
|
||||
toggleMenu,
|
||||
muteAudio,
|
||||
muteParticipantButtonText,
|
||||
participantIds,
|
||||
openDrawerForParticipant,
|
||||
overflowDrawer,
|
||||
raiseContextId,
|
||||
participantActionEllipsisLabel,
|
||||
searchString,
|
||||
stopVideo,
|
||||
youText
|
||||
}: Props) {
|
||||
const renderParticipant = id => (
|
||||
<MeetingParticipantItem
|
||||
askUnmuteText = { askUnmuteText }
|
||||
isHighlighted = { raiseContextId === id }
|
||||
isInBreakoutRoom = { isInBreakoutRoom }
|
||||
key = { id }
|
||||
muteAudio = { muteAudio }
|
||||
muteParticipantButtonText = { muteParticipantButtonText }
|
||||
onContextMenu = { toggleMenu(id) }
|
||||
onLeave = { lowerMenu }
|
||||
openDrawerForParticipant = { openDrawerForParticipant }
|
||||
|
@ -107,6 +109,7 @@ function MeetingParticipantItems({
|
|||
participantActionEllipsisLabel = { participantActionEllipsisLabel }
|
||||
participantID = { id }
|
||||
searchString = { searchString }
|
||||
stopVideo = { stopVideo }
|
||||
youText = { youText } />
|
||||
);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { rejectParticipantAudio } from '../../../av-moderation/actions';
|
||||
import { rejectParticipantAudio, rejectParticipantVideo } from '../../../av-moderation/actions';
|
||||
import participantsPaneTheme from '../../../base/components/themes/participantsPaneTheme.json';
|
||||
import { isToolbarButtonEnabled } from '../../../base/config/functions.web';
|
||||
import { MEDIA_TYPE } from '../../../base/media/constants';
|
||||
|
@ -88,11 +88,14 @@ function MeetingParticipants({
|
|||
const { t } = useTranslation();
|
||||
|
||||
const [ lowerMenu, , toggleMenu, menuEnter, menuLeave, raiseContext ] = useContextMenu();
|
||||
|
||||
const muteAudio = useCallback(id => () => {
|
||||
dispatch(muteRemote(id, MEDIA_TYPE.AUDIO));
|
||||
dispatch(rejectParticipantAudio(id));
|
||||
}, [ dispatch ]);
|
||||
const stopVideo = useCallback(id => () => {
|
||||
dispatch(muteRemote(id, MEDIA_TYPE.VIDEO));
|
||||
dispatch(rejectParticipantVideo(id));
|
||||
}, [ dispatch ]);
|
||||
const [ drawerParticipant, closeDrawer, openDrawerForParticipant ] = useParticipantDrawer();
|
||||
|
||||
// FIXME:
|
||||
|
@ -103,8 +106,6 @@ function MeetingParticipants({
|
|||
// mounted.
|
||||
const participantActionEllipsisLabel = t('participantsPane.actions.moreParticipantOptions');
|
||||
const youText = t('chat.you');
|
||||
const askUnmuteText = t('participantsPane.actions.askUnmute');
|
||||
const muteParticipantButtonText = t('dialog.muteParticipantButton');
|
||||
const isBreakoutRoom = useSelector(isInBreakoutRoom);
|
||||
const visitorsCount = useSelector((state: IReduxState) => state['features/visitors'].count);
|
||||
|
||||
|
@ -136,11 +137,9 @@ function MeetingParticipants({
|
|||
value = { searchString } />
|
||||
<div>
|
||||
<MeetingParticipantItems
|
||||
askUnmuteText = { askUnmuteText }
|
||||
isInBreakoutRoom = { isBreakoutRoom }
|
||||
lowerMenu = { lowerMenu }
|
||||
muteAudio = { muteAudio }
|
||||
muteParticipantButtonText = { muteParticipantButtonText }
|
||||
openDrawerForParticipant = { openDrawerForParticipant }
|
||||
overflowDrawer = { overflowDrawer }
|
||||
participantActionEllipsisLabel = { participantActionEllipsisLabel }
|
||||
|
@ -148,6 +147,7 @@ function MeetingParticipants({
|
|||
participantsCount = { participantsCount }
|
||||
raiseContextId = { raiseContext.entity }
|
||||
searchString = { normalizeAccents(searchString) }
|
||||
stopVideo = { stopVideo }
|
||||
toggleMenu = { toggleMenu }
|
||||
youText = { youText } />
|
||||
</div>
|
||||
|
|
|
@ -14,14 +14,17 @@ interface IProps {
|
|||
* Click handler function.
|
||||
*/
|
||||
onClick: () => void;
|
||||
|
||||
participantID?: string;
|
||||
}
|
||||
|
||||
const ParticipantActionEllipsis = ({ accessibilityLabel, onClick }: IProps) => (
|
||||
const ParticipantActionEllipsis = ({ accessibilityLabel, onClick, participantID }: IProps) => (
|
||||
<Button
|
||||
accessibilityLabel = { accessibilityLabel }
|
||||
icon = { IconDotsHorizontal }
|
||||
onClick = { onClick }
|
||||
size = 'small' />
|
||||
size = 'small'
|
||||
testId = { participantID ? `participant-more-options-${participantID}` : undefined } />
|
||||
);
|
||||
|
||||
export default ParticipantActionEllipsis;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useDispatch } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { approveParticipant } from '../../../av-moderation/actions';
|
||||
import { approveParticipantAudio, approveParticipantVideo } from '../../../av-moderation/actions';
|
||||
import Button from '../../../base/ui/components/web/Button';
|
||||
import { QUICK_ACTION_BUTTON } from '../../constants';
|
||||
|
||||
|
@ -43,6 +43,12 @@ interface IProps {
|
|||
* The name of the participant.
|
||||
*/
|
||||
participantName: string;
|
||||
|
||||
/**
|
||||
* Callback used to stop a participant's video.
|
||||
*/
|
||||
stopVideo: Function;
|
||||
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
|
@ -54,19 +60,22 @@ const useStyles = makeStyles()(theme => {
|
|||
});
|
||||
|
||||
const ParticipantQuickAction = ({
|
||||
askUnmuteText,
|
||||
buttonType,
|
||||
muteAudio,
|
||||
muteParticipantButtonText,
|
||||
participantID,
|
||||
participantName
|
||||
participantName,
|
||||
stopVideo
|
||||
}: IProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const askToUnmute = useCallback(() => {
|
||||
dispatch(approveParticipant(participantID));
|
||||
dispatch(approveParticipantAudio(participantID));
|
||||
}, [ dispatch, participantID ]);
|
||||
|
||||
const allowVideo = useCallback(() => {
|
||||
dispatch(approveParticipantVideo(participantID));
|
||||
}, [ dispatch, participantID ]);
|
||||
|
||||
switch (buttonType) {
|
||||
|
@ -75,10 +84,10 @@ const ParticipantQuickAction = ({
|
|||
<Button
|
||||
accessibilityLabel = { `${t('participantsPane.actions.mute')} ${participantName}` }
|
||||
className = { styles.button }
|
||||
label = { muteParticipantButtonText }
|
||||
label = { t('participantsPane.actions.mute') }
|
||||
onClick = { muteAudio(participantID) }
|
||||
size = 'small'
|
||||
testId = { `mute-${participantID}` } />
|
||||
testId = { `mute-audio-${participantID}` } />
|
||||
);
|
||||
}
|
||||
case QUICK_ACTION_BUTTON.ASK_TO_UNMUTE: {
|
||||
|
@ -86,10 +95,32 @@ const ParticipantQuickAction = ({
|
|||
<Button
|
||||
accessibilityLabel = { `${t('participantsPane.actions.askUnmute')} ${participantName}` }
|
||||
className = { styles.button }
|
||||
label = { askUnmuteText }
|
||||
label = { t('participantsPane.actions.askUnmute') }
|
||||
onClick = { askToUnmute }
|
||||
size = 'small'
|
||||
testId = { `unmute-${participantID}` } />
|
||||
testId = { `unmute-audio-${participantID}` } />
|
||||
);
|
||||
}
|
||||
case QUICK_ACTION_BUTTON.ALLOW_VIDEO: {
|
||||
return (
|
||||
<Button
|
||||
accessibilityLabel = { `${t('participantsPane.actions.askUnmute')} ${participantName}` }
|
||||
className = { styles.button }
|
||||
label = { t('participantsPane.actions.allowVideo') }
|
||||
onClick = { allowVideo }
|
||||
size = 'small'
|
||||
testId = { `unmute-video-${participantID}` } />
|
||||
);
|
||||
}
|
||||
case QUICK_ACTION_BUTTON.STOP_VIDEO: {
|
||||
return (
|
||||
<Button
|
||||
accessibilityLabel = { `${t('participantsPane.actions.mute')} ${participantName}` }
|
||||
className = { styles.button }
|
||||
label = { t('participantsPane.actions.stopVideo') }
|
||||
onClick = { stopVideo(participantID) }
|
||||
size = 'small'
|
||||
testId = { `mute-video-${participantID}` } />
|
||||
);
|
||||
}
|
||||
default: {
|
||||
|
|
|
@ -36,19 +36,23 @@ export const MEDIA_STATE: { [key: string]: MediaState; } = {
|
|||
NONE: 'None'
|
||||
};
|
||||
|
||||
export type QuickActionButtonType = 'Mute' | 'AskToUnmute' | 'None';
|
||||
export type QuickActionButtonType = 'Mute' | 'AskToUnmute' | 'AllowVideo' | 'StopVideo' | 'None';
|
||||
|
||||
/**
|
||||
* Enum of possible participant mute button states.
|
||||
*/
|
||||
export const QUICK_ACTION_BUTTON: {
|
||||
ALLOW_VIDEO: QuickActionButtonType;
|
||||
ASK_TO_UNMUTE: QuickActionButtonType;
|
||||
MUTE: QuickActionButtonType;
|
||||
NONE: QuickActionButtonType;
|
||||
STOP_VIDEO: QuickActionButtonType;
|
||||
} = {
|
||||
ALLOW_VIDEO: 'AllowVideo',
|
||||
MUTE: 'Mute',
|
||||
ASK_TO_UNMUTE: 'AskToUnmute',
|
||||
NONE: 'None'
|
||||
NONE: 'None',
|
||||
STOP_VIDEO: 'StopVideo'
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -131,15 +131,28 @@ export const getParticipantsPaneOpen = (state: IReduxState) => Boolean(getState(
|
|||
*
|
||||
* @param {IParticipant} participant - The participant.
|
||||
* @param {boolean} isAudioMuted - If audio is muted for the participant.
|
||||
* @param {boolean} isVideoMuted - If audio is muted for the participant.
|
||||
* @param {IReduxState} state - The redux state.
|
||||
* @returns {string} - The type of the quick action button.
|
||||
*/
|
||||
export function getQuickActionButtonType(participant: IParticipant, isAudioMuted: Boolean, state: IReduxState) {
|
||||
export function getQuickActionButtonType(
|
||||
participant: IParticipant,
|
||||
isAudioMuted: Boolean,
|
||||
isVideoMuted: Boolean,
|
||||
state: IReduxState) {
|
||||
// handled only by moderators
|
||||
const isVideoForceMuted = isForceMuted(participant, MEDIA_TYPE.VIDEO, state);
|
||||
|
||||
if (isLocalParticipantModerator(state)) {
|
||||
if (!isAudioMuted) {
|
||||
return QUICK_ACTION_BUTTON.MUTE;
|
||||
}
|
||||
if (!isVideoMuted) {
|
||||
return QUICK_ACTION_BUTTON.STOP_VIDEO;
|
||||
}
|
||||
if (isVideoForceMuted) {
|
||||
return QUICK_ACTION_BUTTON.ALLOW_VIDEO;
|
||||
}
|
||||
if (isSupported()(state)) {
|
||||
return QUICK_ACTION_BUTTON.ASK_TO_UNMUTE;
|
||||
}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { approveParticipant } from '../../../av-moderation/actions';
|
||||
import { IconMic } from '../../../base/icons';
|
||||
import ContextMenuItem from '../../../base/ui/components/web/ContextMenuItem';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Whether or not the participant is audio force muted.
|
||||
*/
|
||||
isAudioForceMuted: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the participant is video force muted.
|
||||
*/
|
||||
isVideoForceMuted: boolean,
|
||||
|
||||
/**
|
||||
* The ID for the participant on which the button will act.
|
||||
*/
|
||||
participantID: string
|
||||
}
|
||||
|
||||
const AskToUnmuteButton = ({ isAudioForceMuted, isVideoForceMuted, participantID }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const _onClick = useCallback(() => {
|
||||
dispatch(approveParticipant(participantID));
|
||||
}, [ participantID ]);
|
||||
|
||||
const text = isAudioForceMuted || !isVideoForceMuted
|
||||
? t('participantsPane.actions.askUnmute')
|
||||
: t('participantsPane.actions.allowVideo');
|
||||
|
||||
return (
|
||||
<ContextMenuItem
|
||||
accessibilityLabel = { text }
|
||||
icon = { IconMic }
|
||||
onClick = { _onClick }
|
||||
text = { text } />
|
||||
);
|
||||
};
|
||||
|
||||
export default AskToUnmuteButton;
|
|
@ -0,0 +1,61 @@
|
|||
// @flow
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { approveParticipantAudio, approveParticipantVideo } from '../../../av-moderation/actions';
|
||||
import { IconMic, IconVideo } from '../../../base/icons/svg';
|
||||
import { MEDIA_TYPE, MediaType } from '../../../base/media/constants';
|
||||
import ContextMenuItem from '../../../base/ui/components/web/ContextMenuItem';
|
||||
|
||||
type Props = {
|
||||
|
||||
buttonType: MediaType;
|
||||
|
||||
/**
|
||||
* The ID for the participant on which the button will act.
|
||||
*/
|
||||
participantID: string;
|
||||
};
|
||||
|
||||
const AskToUnmuteButton = ({ buttonType, participantID }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const _onClick = useCallback(() => {
|
||||
if (buttonType === MEDIA_TYPE.AUDIO) {
|
||||
dispatch(approveParticipantAudio(participantID));
|
||||
} else if (buttonType === MEDIA_TYPE.VIDEO) {
|
||||
dispatch(approveParticipantVideo(participantID));
|
||||
}
|
||||
}, [ participantID, buttonType ]);
|
||||
|
||||
const text = useMemo(() => {
|
||||
if (buttonType === MEDIA_TYPE.AUDIO) {
|
||||
return t('participantsPane.actions.askUnmute');
|
||||
} else if (buttonType === MEDIA_TYPE.VIDEO) {
|
||||
return t('participantsPane.actions.allowVideo');
|
||||
}
|
||||
|
||||
return '';
|
||||
}, [ buttonType ]);
|
||||
|
||||
const icon = useMemo(() => {
|
||||
if (buttonType === MEDIA_TYPE.AUDIO) {
|
||||
return IconMic;
|
||||
} else if (buttonType === MEDIA_TYPE.VIDEO) {
|
||||
return IconVideo;
|
||||
}
|
||||
}, [ buttonType ]);
|
||||
|
||||
return (
|
||||
<ContextMenuItem
|
||||
accessibilityLabel = { text }
|
||||
icon = { icon }
|
||||
onClick = { _onClick }
|
||||
testId = { `unmute-${buttonType}-${participantID}` }
|
||||
text = { text } />
|
||||
);
|
||||
};
|
||||
|
||||
export default AskToUnmuteButton;
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
@ -14,14 +14,15 @@ import { MEDIA_TYPE } from '../../../base/media/constants';
|
|||
import { PARTICIPANT_ROLE } from '../../../base/participants/constants';
|
||||
import { getLocalParticipant } from '../../../base/participants/functions';
|
||||
import { IParticipant } from '../../../base/participants/types';
|
||||
import { isParticipantAudioMuted } from '../../../base/tracks/functions';
|
||||
import { isParticipantAudioMuted, isParticipantVideoMuted } from '../../../base/tracks/functions.any';
|
||||
import ContextMenu from '../../../base/ui/components/web/ContextMenu';
|
||||
import ContextMenuItemGroup from '../../../base/ui/components/web/ContextMenuItemGroup';
|
||||
import { getBreakoutRooms, getCurrentRoomId, isInBreakoutRoom } from '../../../breakout-rooms/functions';
|
||||
import { displayVerification } from '../../../e2ee/functions';
|
||||
import { setVolume } from '../../../filmstrip/actions.web';
|
||||
import { isStageFilmstripAvailable } from '../../../filmstrip/functions.web';
|
||||
import { isForceMuted } from '../../../participants-pane/functions';
|
||||
import { QUICK_ACTION_BUTTON } from '../../../participants-pane/constants';
|
||||
import { getQuickActionButtonType, isForceMuted } from '../../../participants-pane/functions';
|
||||
// @ts-ignore
|
||||
import { requestRemoteControl, stopController } from '../../../remote-control';
|
||||
import { showOverflowDrawer } from '../../../toolbox/functions.web';
|
||||
|
@ -139,11 +140,10 @@ const ParticipantContextMenu = ({
|
|||
|
||||
const localParticipant = useSelector(getLocalParticipant);
|
||||
const _isModerator = Boolean(localParticipant?.role === PARTICIPANT_ROLE.MODERATOR);
|
||||
const _isAudioForceMuted = useSelector<IReduxState>(state =>
|
||||
isForceMuted(participant, MEDIA_TYPE.AUDIO, state));
|
||||
const _isVideoForceMuted = useSelector<IReduxState>(state =>
|
||||
isForceMuted(participant, MEDIA_TYPE.VIDEO, state));
|
||||
const _isAudioMuted = useSelector((state: IReduxState) => isParticipantAudioMuted(participant, state));
|
||||
const _isVideoMuted = useSelector((state: IReduxState) => isParticipantVideoMuted(participant, state));
|
||||
const _overflowDrawer: boolean = useSelector(showOverflowDrawer);
|
||||
const { remoteVideoMenu = {}, disableRemoteMute, startSilent, customParticipantMenuButtons }
|
||||
= useSelector((state: IReduxState) => state['features/base/config']);
|
||||
|
@ -172,6 +172,12 @@ const ParticipantContextMenu = ({
|
|||
}
|
||||
, [ thumbnailMenu, _overflowDrawer, drawerParticipant, participant ]);
|
||||
|
||||
const isClickedFromParticipantPane = useMemo(
|
||||
() => !_overflowDrawer && !thumbnailMenu,
|
||||
[ _overflowDrawer, thumbnailMenu ]);
|
||||
const quickActionButtonType = useSelector((state: IReduxState) =>
|
||||
getQuickActionButtonType(participant, _isAudioMuted, _isVideoMuted, state));
|
||||
|
||||
const buttons: JSX.Element[] = [];
|
||||
const buttons2: JSX.Element[] = [];
|
||||
|
||||
|
@ -182,30 +188,44 @@ const ParticipantContextMenu = ({
|
|||
&& !isNaN(_volume);
|
||||
|
||||
if (_isModerator) {
|
||||
if ((thumbnailMenu || _overflowDrawer) && isModerationSupported && _isAudioMuted) {
|
||||
buttons.push(<AskToUnmuteButton
|
||||
isAudioForceMuted = { _isAudioForceMuted }
|
||||
isVideoForceMuted = { _isVideoForceMuted }
|
||||
key = 'ask-unmute'
|
||||
participantID = { _getCurrentParticipantId() } />
|
||||
);
|
||||
if (isModerationSupported) {
|
||||
if (_isAudioMuted
|
||||
&& !(isClickedFromParticipantPane && quickActionButtonType === QUICK_ACTION_BUTTON.ASK_TO_UNMUTE)) {
|
||||
buttons.push(<AskToUnmuteButton
|
||||
buttonType = { MEDIA_TYPE.AUDIO }
|
||||
key = 'ask-unmute'
|
||||
participantID = { _getCurrentParticipantId() } />
|
||||
);
|
||||
}
|
||||
if (_isVideoForceMuted
|
||||
&& !(isClickedFromParticipantPane && quickActionButtonType === QUICK_ACTION_BUTTON.ALLOW_VIDEO)) {
|
||||
buttons.push(<AskToUnmuteButton
|
||||
buttonType = { MEDIA_TYPE.VIDEO }
|
||||
key = 'allow-video'
|
||||
participantID = { _getCurrentParticipantId() } />
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!disableRemoteMute) {
|
||||
buttons.push(
|
||||
<MuteButton
|
||||
key = 'mute'
|
||||
participantID = { _getCurrentParticipantId() } />
|
||||
);
|
||||
if (!(isClickedFromParticipantPane && quickActionButtonType === QUICK_ACTION_BUTTON.MUTE)) {
|
||||
buttons.push(
|
||||
<MuteButton
|
||||
key = 'mute'
|
||||
participantID = { _getCurrentParticipantId() } />
|
||||
);
|
||||
}
|
||||
buttons.push(
|
||||
<MuteEveryoneElseButton
|
||||
key = 'mute-others'
|
||||
participantID = { _getCurrentParticipantId() } />
|
||||
);
|
||||
buttons.push(
|
||||
<MuteVideoButton
|
||||
key = 'mute-video'
|
||||
participantID = { _getCurrentParticipantId() } />
|
||||
);
|
||||
if (!(isClickedFromParticipantPane && quickActionButtonType === QUICK_ACTION_BUTTON.STOP_VIDEO)) {
|
||||
buttons.push(
|
||||
<MuteVideoButton
|
||||
key = 'mute-video'
|
||||
participantID = { _getCurrentParticipantId() } />
|
||||
);
|
||||
}
|
||||
buttons.push(
|
||||
<MuteEveryoneElsesVideoButton
|
||||
key = 'mute-others-video'
|
||||
|
|
Loading…
Reference in New Issue