mirror of https://github.com/jitsi/jitsi-meet
pull/13596/head
jitsi-meet_8833
parent
470e987fad
commit
1b7a81afa5
@ -1,43 +1,42 @@ |
||||
import React, { useCallback } from 'react'; |
||||
import { WithTranslation } from 'react-i18next'; |
||||
import { connect } from 'react-redux'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { useDispatch } from 'react-redux'; |
||||
|
||||
import { IStore } from '../../../app/types'; |
||||
import { translate } from '../../../base/i18n/functions'; |
||||
import { IconInfoCircle } from '../../../base/icons/svg'; |
||||
import ContextMenuItem from '../../../base/ui/components/web/ContextMenuItem'; |
||||
import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants'; |
||||
import { renderConnectionStatus } from '../../actions.web'; |
||||
|
||||
interface IProps extends WithTranslation { |
||||
|
||||
/** |
||||
* The Redux dispatch function. |
||||
*/ |
||||
dispatch: IStore['dispatch']; |
||||
|
||||
/** |
||||
* The ID of the participant for which to show connection stats. |
||||
*/ |
||||
participantId: string; |
||||
} |
||||
|
||||
|
||||
import { IButtonProps } from '../../types'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button that shows |
||||
* the connection status for the given participant. |
||||
* |
||||
* @returns {JSX.Element} |
||||
*/ |
||||
const ConnectionStatusButton = ({ |
||||
dispatch, |
||||
t |
||||
}: IProps) => { |
||||
const onClick = useCallback(e => { |
||||
notifyClick, |
||||
notifyMode |
||||
}: IButtonProps): JSX.Element => { |
||||
const { t } = useTranslation(); |
||||
const dispatch = useDispatch(); |
||||
|
||||
const handleClick = useCallback(e => { |
||||
e.stopPropagation(); |
||||
notifyClick?.(); |
||||
if (notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) { |
||||
return; |
||||
} |
||||
dispatch(renderConnectionStatus(true)); |
||||
}, [ dispatch ]); |
||||
}, [ dispatch, notifyClick, notifyMode ]); |
||||
|
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('videothumbnail.connectionInfo') } |
||||
icon = { IconInfoCircle } |
||||
onClick = { onClick } |
||||
onClick = { handleClick } |
||||
text = { t('videothumbnail.connectionInfo') } /> |
||||
); |
||||
}; |
||||
|
||||
export default translate(connect()(ConnectionStatusButton)); |
||||
export default ConnectionStatusButton; |
||||
|
@ -1,52 +1,56 @@ |
||||
import React from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import React, { useCallback, useMemo } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
|
||||
import { translate } from '../../../base/i18n/functions'; |
||||
import { IReduxState } from '../../../app/types'; |
||||
import { openDialog } from '../../../base/dialog/actions'; |
||||
import { IconModerator } from '../../../base/icons/svg'; |
||||
import { PARTICIPANT_ROLE } from '../../../base/participants/constants'; |
||||
import { getLocalParticipant, getParticipantById, isParticipantModerator } from '../../../base/participants/functions'; |
||||
import ContextMenuItem from '../../../base/ui/components/web/ContextMenuItem'; |
||||
import AbstractGrantModeratorButton, { IProps, _mapStateToProps } from '../AbstractGrantModeratorButton'; |
||||
import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants'; |
||||
import { IButtonProps } from '../../types'; |
||||
|
||||
import GrantModeratorDialog from './GrantModeratorDialog'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button for granting |
||||
* moderator to a participant. |
||||
* |
||||
* @returns {JSX.Element|null} |
||||
*/ |
||||
class GrantModeratorButton extends AbstractGrantModeratorButton { |
||||
/** |
||||
* Instantiates a new {@code GrantModeratorButton}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: IProps) { |
||||
super(props); |
||||
|
||||
this._handleClick = this._handleClick.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { t, visible } = this.props; |
||||
|
||||
if (!visible) { |
||||
return null; |
||||
const GrantModeratorButton = ({ |
||||
notifyClick, |
||||
notifyMode, |
||||
participantID |
||||
}: IButtonProps): JSX.Element | null => { |
||||
const { t } = useTranslation(); |
||||
const dispatch = useDispatch(); |
||||
const localParticipant = useSelector(getLocalParticipant); |
||||
const targetParticipant = useSelector((state: IReduxState) => getParticipantById(state, participantID)); |
||||
const visible = useMemo(() => Boolean(localParticipant?.role === PARTICIPANT_ROLE.MODERATOR) |
||||
&& !isParticipantModerator(targetParticipant), [ isParticipantModerator, localParticipant, targetParticipant ]); |
||||
|
||||
const handleClick = useCallback(() => { |
||||
notifyClick?.(); |
||||
if (notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) { |
||||
return; |
||||
} |
||||
dispatch(openDialog(GrantModeratorDialog, { participantID })); |
||||
}, [ dispatch, notifyClick, notifyMode, participantID ]); |
||||
|
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.grantModerator') } |
||||
className = 'grantmoderatorlink' |
||||
icon = { IconModerator } |
||||
// eslint-disable-next-line react/jsx-handler-names
|
||||
onClick = { this._handleClick } |
||||
text = { t('videothumbnail.grantModerator') } /> |
||||
); |
||||
if (!visible) { |
||||
return null; |
||||
} |
||||
|
||||
_handleClick: () => void; |
||||
} |
||||
|
||||
export default translate(connect(_mapStateToProps)(GrantModeratorButton)); |
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.grantModerator') } |
||||
className = 'grantmoderatorlink' |
||||
icon = { IconModerator } |
||||
onClick = { handleClick } |
||||
text = { t('videothumbnail.grantModerator') } /> |
||||
); |
||||
}; |
||||
|
||||
export default GrantModeratorButton; |
||||
|
@ -1,54 +1,46 @@ |
||||
import React from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import React, { useCallback } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { useDispatch } from 'react-redux'; |
||||
|
||||
import { translate } from '../../../base/i18n/functions'; |
||||
import { openDialog } from '../../../base/dialog/actions'; |
||||
import { IconUserDeleted } from '../../../base/icons/svg'; |
||||
import ContextMenuItem from '../../../base/ui/components/web/ContextMenuItem'; |
||||
import AbstractKickButton, { IProps } from '../AbstractKickButton'; |
||||
import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants'; |
||||
import { IButtonProps } from '../../types'; |
||||
|
||||
import KickRemoteParticipantDialog from './KickRemoteParticipantDialog'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button for kicking out |
||||
* a participant from the conference. |
||||
* |
||||
* NOTE: At the time of writing this is a button that doesn't use the |
||||
* {@code AbstractButton} base component, but is inherited from the same |
||||
* super class ({@code AbstractKickButton} that extends {@code AbstractButton}) |
||||
* for the sake of code sharing between web and mobile. Once web uses the |
||||
* {@code AbstractButton} base component, this can be fully removed. |
||||
* @returns {JSX.Element} |
||||
*/ |
||||
class KickButton extends AbstractKickButton { |
||||
/** |
||||
* Instantiates a new {@code Component}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: IProps) { |
||||
super(props); |
||||
|
||||
this._handleClick = this._handleClick.bind(this); |
||||
} |
||||
const KickButton = ({ |
||||
notifyClick, |
||||
notifyMode, |
||||
participantID |
||||
}: IButtonProps): JSX.Element => { |
||||
const { t } = useTranslation(); |
||||
const dispatch = useDispatch(); |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { participantID, t } = this.props; |
||||
const handleClick = useCallback(() => { |
||||
notifyClick?.(); |
||||
if (notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) { |
||||
return; |
||||
} |
||||
dispatch(openDialog(KickRemoteParticipantDialog, { participantID })); |
||||
}, [ dispatch, notifyClick, notifyMode, participantID ]); |
||||
|
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('videothumbnail.kick') } |
||||
className = 'kicklink' |
||||
icon = { IconUserDeleted } |
||||
id = { `ejectlink_${participantID}` } |
||||
// eslint-disable-next-line react/jsx-handler-names
|
||||
onClick = { this._handleClick } |
||||
text = { t('videothumbnail.kick') } /> |
||||
); |
||||
} |
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('videothumbnail.kick') } |
||||
className = 'kicklink' |
||||
icon = { IconUserDeleted } |
||||
id = { `ejectlink_${participantID}` } |
||||
onClick = { handleClick } |
||||
text = { t('videothumbnail.kick') } /> |
||||
); |
||||
}; |
||||
|
||||
_handleClick: () => void; |
||||
} |
||||
export default translate(connect()(KickButton)); |
||||
export default KickButton; |
||||
|
@ -1,59 +1,65 @@ |
||||
import React from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import React, { useCallback, useMemo } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
|
||||
import { translate } from '../../../base/i18n/functions'; |
||||
import { createRemoteVideoMenuButtonEvent } from '../../../analytics/AnalyticsEvents'; |
||||
import { sendAnalytics } from '../../../analytics/functions'; |
||||
import { IReduxState } from '../../../app/types'; |
||||
import { rejectParticipantAudio } from '../../../av-moderation/actions'; |
||||
import { IconMicSlash } from '../../../base/icons/svg'; |
||||
import { MEDIA_TYPE } from '../../../base/media/constants'; |
||||
import { isRemoteTrackMuted } from '../../../base/tracks/functions.any'; |
||||
import ContextMenuItem from '../../../base/ui/components/web/ContextMenuItem'; |
||||
import AbstractMuteButton, { IProps, _mapStateToProps } from '../AbstractMuteButton'; |
||||
|
||||
import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants'; |
||||
import { muteRemote } from '../../actions.any'; |
||||
import { IButtonProps } from '../../types'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button for audio muting |
||||
* a participant in the conference. |
||||
* |
||||
* NOTE: At the time of writing this is a button that doesn't use the |
||||
* {@code AbstractButton} base component, but is inherited from the same |
||||
* super class ({@code AbstractMuteButton} that extends {@code AbstractButton}) |
||||
* for the sake of code sharing between web and mobile. Once web uses the |
||||
* {@code AbstractButton} base component, this can be fully removed. |
||||
* @returns {JSX.Element|null} |
||||
*/ |
||||
class MuteButton extends AbstractMuteButton { |
||||
/** |
||||
* Instantiates a new {@code Component}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: IProps) { |
||||
super(props); |
||||
|
||||
this._handleClick = this._handleClick.bind(this); |
||||
} |
||||
const MuteButton = ({ |
||||
notifyClick, |
||||
notifyMode, |
||||
participantID |
||||
}: IButtonProps): JSX.Element | null => { |
||||
const { t } = useTranslation(); |
||||
const dispatch = useDispatch(); |
||||
const tracks = useSelector((state: IReduxState) => state['features/base/tracks']); |
||||
const audioTrackMuted = useMemo( |
||||
() => isRemoteTrackMuted(tracks, MEDIA_TYPE.AUDIO, participantID), |
||||
[ isRemoteTrackMuted, participantID, tracks ] |
||||
); |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { _audioTrackMuted, t } = this.props; |
||||
|
||||
if (_audioTrackMuted) { |
||||
return null; |
||||
const handleClick = useCallback(() => { |
||||
notifyClick?.(); |
||||
if (notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) { |
||||
return; |
||||
} |
||||
sendAnalytics(createRemoteVideoMenuButtonEvent( |
||||
'mute', |
||||
{ |
||||
'participant_id': participantID |
||||
})); |
||||
|
||||
dispatch(muteRemote(participantID, MEDIA_TYPE.AUDIO)); |
||||
dispatch(rejectParticipantAudio(participantID)); |
||||
}, [ dispatch, notifyClick, notifyMode, participantID, sendAnalytics ]); |
||||
|
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('dialog.muteParticipantButton') } |
||||
className = 'mutelink' |
||||
icon = { IconMicSlash } |
||||
// eslint-disable-next-line react/jsx-handler-names
|
||||
onClick = { this._handleClick } |
||||
text = { t('dialog.muteParticipantButton') } /> |
||||
); |
||||
if (audioTrackMuted) { |
||||
return null; |
||||
} |
||||
|
||||
_handleClick: () => void; |
||||
} |
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('dialog.muteParticipantButton') } |
||||
className = 'mutelink' |
||||
icon = { IconMicSlash } |
||||
onClick = { handleClick } |
||||
text = { t('dialog.muteParticipantButton') } /> |
||||
); |
||||
}; |
||||
|
||||
export default translate(connect(_mapStateToProps)(MuteButton)); |
||||
export default MuteButton; |
||||
|
@ -1,48 +1,48 @@ |
||||
import React from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import React, { useCallback } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { useDispatch } from 'react-redux'; |
||||
|
||||
import { translate } from '../../../base/i18n/functions'; |
||||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents'; |
||||
import { sendAnalytics } from '../../../analytics/functions'; |
||||
import { openDialog } from '../../../base/dialog/actions'; |
||||
import { IconMicSlash } from '../../../base/icons/svg'; |
||||
import ContextMenuItem from '../../../base/ui/components/web/ContextMenuItem'; |
||||
import AbstractMuteEveryoneElseButton, { IProps } from '../AbstractMuteEveryoneElseButton'; |
||||
import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants'; |
||||
import { IButtonProps } from '../../types'; |
||||
|
||||
import MuteEveryoneDialog from './MuteEveryoneDialog'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button for audio muting |
||||
* every participant in the conference except the one with the given |
||||
* participantID. |
||||
* |
||||
* @returns {JSX.Element} |
||||
*/ |
||||
class MuteEveryoneElseButton extends AbstractMuteEveryoneElseButton { |
||||
/** |
||||
* Instantiates a new {@code Component}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: IProps) { |
||||
super(props); |
||||
|
||||
this._handleClick = this._handleClick.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { t } = this.props; |
||||
const MuteEveryoneElseButton = ({ |
||||
notifyClick, |
||||
notifyMode, |
||||
participantID |
||||
}: IButtonProps): JSX.Element => { |
||||
const { t } = useTranslation(); |
||||
const dispatch = useDispatch(); |
||||
|
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.muteEveryoneElse') } |
||||
icon = { IconMicSlash } |
||||
// eslint-disable-next-line react/jsx-handler-names
|
||||
onClick = { this._handleClick } |
||||
text = { t('videothumbnail.domuteOthers') } /> |
||||
); |
||||
} |
||||
const handleClick = useCallback(() => { |
||||
notifyClick?.(); |
||||
if (notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) { |
||||
return; |
||||
} |
||||
sendAnalytics(createToolbarEvent('mute.everyoneelse.pressed')); |
||||
dispatch(openDialog(MuteEveryoneDialog, { exclude: [ participantID ] })); |
||||
}, [ dispatch, notifyMode, notifyClick, participantID, sendAnalytics ]); |
||||
|
||||
_handleClick: () => void; |
||||
} |
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.muteEveryoneElse') } |
||||
icon = { IconMicSlash } |
||||
onClick = { handleClick } |
||||
text = { t('videothumbnail.domuteOthers') } /> |
||||
); |
||||
}; |
||||
|
||||
export default translate(connect()(MuteEveryoneElseButton)); |
||||
export default MuteEveryoneElseButton; |
||||
|
@ -1,48 +1,48 @@ |
||||
import React from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import React, { useCallback } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { useDispatch } from 'react-redux'; |
||||
|
||||
import { translate } from '../../../base/i18n/functions'; |
||||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents'; |
||||
import { sendAnalytics } from '../../../analytics/functions'; |
||||
import { openDialog } from '../../../base/dialog/actions'; |
||||
import { IconVideoOff } from '../../../base/icons/svg'; |
||||
import ContextMenuItem from '../../../base/ui/components/web/ContextMenuItem'; |
||||
import AbstractMuteEveryoneElsesVideoButton, { IProps } from '../AbstractMuteEveryoneElsesVideoButton'; |
||||
import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants'; |
||||
import { IButtonProps } from '../../types'; |
||||
|
||||
import MuteEveryonesVideoDialog from './MuteEveryonesVideoDialog'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button for audio muting |
||||
* every participant in the conference except the one with the given |
||||
* participantID. |
||||
* |
||||
* @returns {JSX.Element} |
||||
*/ |
||||
class MuteEveryoneElsesVideoButton extends AbstractMuteEveryoneElsesVideoButton { |
||||
/** |
||||
* Instantiates a new {@code Component}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: IProps) { |
||||
super(props); |
||||
|
||||
this._handleClick = this._handleClick.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { t } = this.props; |
||||
const MuteEveryoneElsesVideoButton = ({ |
||||
notifyClick, |
||||
notifyMode, |
||||
participantID |
||||
}: IButtonProps): JSX.Element => { |
||||
const { t } = useTranslation(); |
||||
const dispatch = useDispatch(); |
||||
|
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.muteEveryoneElsesVideoStream') } |
||||
icon = { IconVideoOff } |
||||
// eslint-disable-next-line react/jsx-handler-names
|
||||
onClick = { this._handleClick } |
||||
text = { t('videothumbnail.domuteVideoOfOthers') } /> |
||||
); |
||||
} |
||||
const handleClick = useCallback(() => { |
||||
notifyClick?.(); |
||||
if (notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) { |
||||
return; |
||||
} |
||||
sendAnalytics(createToolbarEvent('mute.everyoneelsesvideo.pressed')); |
||||
dispatch(openDialog(MuteEveryonesVideoDialog, { exclude: [ participantID ] })); |
||||
}, [ notifyClick, notifyMode, participantID ]); |
||||
|
||||
_handleClick: () => void; |
||||
} |
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.muteEveryoneElsesVideoStream') } |
||||
icon = { IconVideoOff } |
||||
onClick = { handleClick } |
||||
text = { t('videothumbnail.domuteVideoOfOthers') } /> |
||||
); |
||||
}; |
||||
|
||||
export default translate(connect()(MuteEveryoneElsesVideoButton)); |
||||
export default MuteEveryoneElsesVideoButton; |
||||
|
@ -1,58 +1,66 @@ |
||||
import React from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import React, { useCallback, useMemo } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
|
||||
import { translate } from '../../../base/i18n/functions'; |
||||
import { createRemoteVideoMenuButtonEvent } from '../../../analytics/AnalyticsEvents'; |
||||
import { sendAnalytics } from '../../../analytics/functions'; |
||||
import { IReduxState } from '../../../app/types'; |
||||
import { openDialog } from '../../../base/dialog/actions'; |
||||
import { IconVideoOff } from '../../../base/icons/svg'; |
||||
import { MEDIA_TYPE } from '../../../base/media/constants'; |
||||
import { isRemoteTrackMuted } from '../../../base/tracks/functions.any'; |
||||
import ContextMenuItem from '../../../base/ui/components/web/ContextMenuItem'; |
||||
import AbstractMuteVideoButton, { IProps, _mapStateToProps } from '../AbstractMuteVideoButton'; |
||||
import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants'; |
||||
import { IButtonProps } from '../../types'; |
||||
|
||||
import MuteRemoteParticipantsVideoDialog from './MuteRemoteParticipantsVideoDialog'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button for disabling |
||||
* the camera of a participant in the conference. |
||||
* |
||||
* NOTE: At the time of writing this is a button that doesn't use the |
||||
* {@code AbstractButton} base component, but is inherited from the same |
||||
* super class ({@code AbstractMuteVideoButton} that extends {@code AbstractButton}) |
||||
* for the sake of code sharing between web and mobile. Once web uses the |
||||
* {@code AbstractButton} base component, this can be fully removed. |
||||
* @returns {JSX.Element|null} |
||||
*/ |
||||
class MuteVideoButton extends AbstractMuteVideoButton { |
||||
/** |
||||
* Instantiates a new {@code Component}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: IProps) { |
||||
super(props); |
||||
|
||||
this._handleClick = this._handleClick.bind(this); |
||||
} |
||||
const MuteVideoButton = ({ |
||||
notifyClick, |
||||
notifyMode, |
||||
participantID |
||||
}: IButtonProps): JSX.Element | null => { |
||||
const { t } = useTranslation(); |
||||
const dispatch = useDispatch(); |
||||
const tracks = useSelector((state: IReduxState) => state['features/base/tracks']); |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { _videoTrackMuted, t } = this.props; |
||||
|
||||
if (_videoTrackMuted) { |
||||
return null; |
||||
const videoTrackMuted = useMemo( |
||||
() => isRemoteTrackMuted(tracks, MEDIA_TYPE.VIDEO, participantID), |
||||
[ isRemoteTrackMuted, participantID, tracks ] |
||||
); |
||||
|
||||
const handleClick = useCallback(() => { |
||||
notifyClick?.(); |
||||
if (notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) { |
||||
return; |
||||
} |
||||
sendAnalytics(createRemoteVideoMenuButtonEvent( |
||||
'video.mute.button', |
||||
{ |
||||
'participant_id': participantID |
||||
})); |
||||
|
||||
dispatch(openDialog(MuteRemoteParticipantsVideoDialog, { participantID })); |
||||
}, [ dispatch, notifyClick, notifyClick, participantID, sendAnalytics ]); |
||||
|
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('participantsPane.actions.stopVideo') } |
||||
className = 'mutevideolink' |
||||
icon = { IconVideoOff } |
||||
// eslint-disable-next-line react/jsx-handler-names
|
||||
onClick = { this._handleClick } |
||||
text = { t('participantsPane.actions.stopVideo') } /> |
||||
); |
||||
if (videoTrackMuted) { |
||||
return null; |
||||
} |
||||
|
||||
_handleClick: () => void; |
||||
} |
||||
return ( |
||||
<ContextMenuItem |
||||
accessibilityLabel = { t('participantsPane.actions.stopVideo') } |
||||
className = 'mutevideolink' |
||||
icon = { IconVideoOff } |
||||
onClick = { handleClick } |
||||
text = { t('participantsPane.actions.stopVideo') } /> |
||||
); |
||||
}; |
||||
|
||||
export default translate(connect(_mapStateToProps)(MuteVideoButton)); |
||||
export default MuteVideoButton; |
||||
|
@ -0,0 +1,18 @@ |
||||
export interface IButtonProps { |
||||
|
||||
/** |
||||
* Callback to execute when the button is clicked. |
||||
*/ |
||||
notifyClick?: Function; |
||||
|
||||
/** |
||||
* Notify mode for the `participantMenuButtonClicked` event - |
||||
* whether to only notify or to also prevent button click routine. |
||||
*/ |
||||
notifyMode?: string; |
||||
|
||||
/** |
||||
* The ID of the participant that's linked to the button. |
||||
*/ |
||||
participantID: string; |
||||
} |
Loading…
Reference in new issue