fix(av-moderation) Improve advanced moderation (#10004)

* fix(av-moderation) Improve advanced moderation

Hide moderator label on disasbleModeratorIndicator
 - On disasbleModeratorIndicator config hide moderator label from participants pane

Add Ask to Unmute button to mobile web

* Fix lint error
This commit is contained in:
robertpin 2021-09-28 11:46:20 +03:00 committed by GitHub
parent a91b2c91dd
commit 700e809439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 15 deletions

View File

@ -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<Props> {
render() {
const {
_audioMediaState,
_disableModeratorIndicator,
_displayName,
_isModerator,
_local,
@ -142,6 +148,7 @@ class MeetingParticipantItem extends PureComponent<Props> {
return (
<ParticipantItem
audioMediaState = { _audioMediaState }
disableModeratorIndicator = { _disableModeratorIndicator }
displayName = { _displayName }
isKnockingParticipant = { false }
isModerator = { _isModerator }
@ -171,9 +178,11 @@ function mapStateToProps(state, ownProps): Object {
const _isVideoMuted = isParticipantVideoMuted(participant, state);
const audioMediaState = getParticipantAudioMediaState(participant, _isAudioMuted, state);
const videoMediaState = getParticipantVideoMediaState(participant, _isVideoMuted, state);
const { disableModeratorIndicator } = state['features/base/config'];
return {
_audioMediaState: audioMediaState,
_disableModeratorIndicator: disableModeratorIndicator,
_displayName: getParticipantDisplayName(state, participant?.id),
_isAudioMuted,
_isFakeParticipant: Boolean(participant?.isFakeParticipant),

View File

@ -24,6 +24,11 @@ type Props = {
*/
children?: Node,
/**
* Whether or not to disable the moderator indicator.
*/
disableModeratorIndicator?: boolean,
/**
* The name of the participant. Used for showing lobby names.
*/
@ -73,6 +78,7 @@ type Props = {
function ParticipantItem({
children,
displayName,
disableModeratorIndicator,
isKnockingParticipant,
isModerator,
local,
@ -101,7 +107,9 @@ function ParticipantItem({
</Text>
{ local ? <Text style = { styles.isLocal }>({t('chat.you')})</Text> : null }
</View>
{isModerator && <Text style = { styles.moderatorLabel }>{t('videothumbnail.moderator')}</Text>}
{isModerator && !disableModeratorIndicator
&& <Text style = { styles.moderatorLabel }>{t('videothumbnail.moderator')}</Text>
}
</View>
{
!isKnockingParticipant

View File

@ -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<Props, State> {
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<Props, State> {
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<Props, State> {
*/
render() {
const {
_isAudioForceMuted,
_isLocalModerator,
_isChatButtonEnabled,
_isParticipantModerator,
_isParticipantVideoMuted,
_isParticipantAudioMuted,
_isVideoForceMuted,
_localVideoOwner,
_participant,
_volume = 1,
@ -416,6 +446,16 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
{_isLocalModerator && (
<ContextMenuItemGroup>
<>
{overflowDrawer && (_isAudioForceMuted || _isVideoForceMuted)
&& <ContextMenuItem onClick = { this._onAskToUnmute }>
<ContextMenuIcon src = { IconMicrophone } />
<span>
{t(_isAudioForceMuted
? 'participantsPane.actions.askUnmute'
: 'participantsPane.actions.allowVideo')}
</span>
</ContextMenuItem>
}
{
!_isParticipantAudioMuted && overflowDrawer
&& <ContextMenuItem onClick = { muteAudio(_participant) }>
@ -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

View File

@ -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({
<ParticipantItem
actionsTrigger = { ACTION_TRIGGER.HOVER }
audioMediaState = { audioMediaState }
disableModeratorIndicator = { _disableModeratorIndicator }
displayName = { _displayName }
isHighlighted = { isHighlighted }
isModerator = { isParticipantModerator(_participant) }
@ -279,17 +285,20 @@ function _mapStateToProps(state, ownProps): Object {
const _audioTrack = participantID === localParticipantId
? getLocalAudioTrack(tracks) : getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, participantID);
const { disableModeratorIndicator } = state['features/base/config'];
return {
_audioMediaState,
_audioTrack,
_videoMediaState,
_disableModeratorIndicator: disableModeratorIndicator,
_displayName: getParticipantDisplayName(state, participant?.id),
_local: Boolean(participant?.local),
_localVideoOwner: Boolean(ownerId === localParticipantId),
_participant: participant,
_participantID: participant?.id,
_quickActionButtonType,
_raisedHand: Boolean(participant?.raisedHand)
_raisedHand: Boolean(participant?.raisedHand),
_videoMediaState
};
}

View File

@ -51,6 +51,11 @@ type Props = {
*/
children: Node,
/**
* Whether or not to disable the moderator indicator.
*/
disableModeratorIndicator: boolean,
/**
* The name of the participant. Used for showing lobby names.
*/
@ -119,20 +124,21 @@ type Props = {
* @returns {ReactNode}
*/
function ParticipantItem({
children,
isHighlighted,
isModerator,
onLeave,
actionsTrigger = ACTION_TRIGGER.HOVER,
audioMediaState = MEDIA_STATE.NONE,
videoMediaState = MEDIA_STATE.NONE,
children,
disableModeratorIndicator,
displayName,
participantID,
isHighlighted,
isModerator,
local,
onLeave,
openDrawerForParticipant,
overflowDrawer,
participantID,
raisedHand,
t,
videoMediaState = MEDIA_STATE.NONE,
youText
}: Props) {
const ParticipantActions = Actions[actionsTrigger];
@ -162,7 +168,7 @@ function ParticipantItem({
</ParticipantName>
{ local ? <span>&nbsp;({ youText })</span> : null }
</ParticipantNameContainer>
{isModerator && <ModeratorLabel>
{isModerator && !disableModeratorIndicator && <ModeratorLabel>
{t('videothumbnail.moderator')}
</ModeratorLabel>}
</ParticipantDetailsContainer>