diff --git a/react/features/participants-pane/components/native/MeetingParticipantItem.js b/react/features/participants-pane/components/native/MeetingParticipantItem.js index 400ad954c..24f7e7102 100644 --- a/react/features/participants-pane/components/native/MeetingParticipantItem.js +++ b/react/features/participants-pane/components/native/MeetingParticipantItem.js @@ -28,6 +28,11 @@ type Props = { */ _audioMediaState: MediaState, + /** + * Whether or not to disable the moderator indicator. + */ + _disableModeratorIndicator: boolean, + /** * The display name of the participant. */ @@ -131,6 +136,7 @@ class MeetingParticipantItem extends PureComponent { render() { const { _audioMediaState, + _disableModeratorIndicator, _displayName, _isModerator, _local, @@ -142,6 +148,7 @@ class MeetingParticipantItem extends PureComponent { return ( { local ? ({t('chat.you')}) : null } - {isModerator && {t('videothumbnail.moderator')}} + {isModerator && !disableModeratorIndicator + && {t('videothumbnail.moderator')} + } { !isKnockingParticipant diff --git a/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js b/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js index 0606bd0c2..6cf03a7e3 100644 --- a/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js +++ b/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js @@ -2,6 +2,7 @@ import { withStyles } from '@material-ui/core/styles'; import React, { Component } from 'react'; +import { approveParticipant } from '../../../av-moderation/actions'; import { Avatar } from '../../../base/avatar'; import { isToolbarButtonEnabled } from '../../../base/config/functions.web'; import { openDialog } from '../../../base/dialog'; @@ -12,10 +13,12 @@ import { IconCrown, IconMessage, IconMicDisabled, + IconMicrophone, IconMuteEveryoneElse, IconShareVideo, IconVideoOff } from '../../../base/icons'; +import { MEDIA_TYPE } from '../../../base/media'; import { getLocalParticipant, getParticipantByIdOrUndefined, @@ -31,7 +34,7 @@ import { Drawer, DrawerPortal } from '../../../toolbox/components/web'; import { GrantModeratorDialog, KickRemoteParticipantDialog, MuteEveryoneDialog } from '../../../video-menu'; import { VolumeSlider } from '../../../video-menu/components/web'; import MuteRemoteParticipantsVideoDialog from '../../../video-menu/components/web/MuteRemoteParticipantsVideoDialog'; -import { getComputedOuterHeight } from '../../functions'; +import { getComputedOuterHeight, isForceMuted } from '../../functions'; import { ContextMenu, @@ -43,6 +46,11 @@ import { type Props = { + /** + * Whether or not the participant is audio force muted. + */ + _isAudioForceMuted: boolean, + /** * True if the local participant is moderator and false otherwise. */ @@ -68,6 +76,11 @@ type Props = { */ _isParticipantAudioMuted: boolean, + /** + * Whether or not the participant is video force muted. + */ + _isVideoForceMuted: boolean, + /** * Shared video local participant owner. */ @@ -206,6 +219,7 @@ class MeetingParticipantContextMenu extends Component { this._onSendPrivateMessage = this._onSendPrivateMessage.bind(this); this._position = this._position.bind(this); this._onVolumeChange = this._onVolumeChange.bind(this); + this._onAskToUnmute = this._onAskToUnmute.bind(this); } _getCurrentParticipantId: () => string; @@ -344,6 +358,20 @@ class MeetingParticipantContextMenu extends Component { dispatch(setVolume(id, value)); } + _onAskToUnmute: () => void; + + /** + * Handles click on ask to unmute. + * + * @returns {void} + */ + _onAskToUnmute() { + const { _participant, dispatch } = this.props; + const { id } = _participant; + + dispatch(approveParticipant(id)); + } + /** * Implements React Component's componentDidMount. * @@ -373,11 +401,13 @@ class MeetingParticipantContextMenu extends Component { */ render() { const { + _isAudioForceMuted, _isLocalModerator, _isChatButtonEnabled, _isParticipantModerator, _isParticipantVideoMuted, _isParticipantAudioMuted, + _isVideoForceMuted, _localVideoOwner, _participant, _volume = 1, @@ -416,6 +446,16 @@ class MeetingParticipantContextMenu extends Component { {_isLocalModerator && ( <> + {overflowDrawer && (_isAudioForceMuted || _isVideoForceMuted) + && + + + {t(_isAudioForceMuted + ? 'participantsPane.actions.askUnmute' + : 'participantsPane.actions.allowVideo')} + + + } { !_isParticipantAudioMuted && overflowDrawer && @@ -542,11 +582,13 @@ function _mapStateToProps(state, ownProps): Object { const isLocal = participant?.local ?? true; return { + _isAudioForceMuted: isForceMuted(participant, MEDIA_TYPE.AUDIO, state), _isLocalModerator, _isChatButtonEnabled, _isParticipantModerator, _isParticipantVideoMuted, _isParticipantAudioMuted, + _isVideoForceMuted: isForceMuted(participant, MEDIA_TYPE.VIDEO, state), _localVideoOwner: Boolean(ownerId === localParticipantId), _participant: participant, _volume: isLocal ? undefined : id ? participantsVolume[id] : undefined diff --git a/react/features/participants-pane/components/web/MeetingParticipantItem.js b/react/features/participants-pane/components/web/MeetingParticipantItem.js index 001a2d554..0f8cf4218 100644 --- a/react/features/participants-pane/components/web/MeetingParticipantItem.js +++ b/react/features/participants-pane/components/web/MeetingParticipantItem.js @@ -42,10 +42,9 @@ type Props = { _audioTrack: ?Object, /** - * Media state for video. + * Whether or not to disable the moderator indicator. */ - _videoMediaState: MediaState, - + _disableModeratorIndicator: boolean, /** * The display name of the participant. @@ -85,6 +84,11 @@ type Props = { */ _raisedHand: boolean, + /** + * Media state for video. + */ + _videoMediaState: MediaState, + /** * The translated ask unmute text for the qiuck action buttons. */ @@ -156,7 +160,7 @@ type Props = { function MeetingParticipantItem({ _audioMediaState, _audioTrack, - _videoMediaState, + _disableModeratorIndicator, _displayName, _local, _localVideoOwner, @@ -164,6 +168,7 @@ function MeetingParticipantItem({ _participantID, _quickActionButtonType, _raisedHand, + _videoMediaState, askUnmuteText, isHighlighted, muteAudio, @@ -219,6 +224,7 @@ function MeetingParticipantItem({ { local ?  ({ youText }) : null } - {isModerator && + {isModerator && !disableModeratorIndicator && {t('videothumbnail.moderator')} }