feat(toolbox): implement buttons using ToolboxItem
Currently the following are implemented: - AudioMuteButton - HangupButton - VideoMuteButton In order to implement these new buttons a new abstract class was introduced, which abstracts the ToolboxItem into a button with enough hooks so a stateful and a stateless version of it can be created. This patch only adds the stateful implementation of the aforementioned buttons.
This commit is contained in:
parent
8d94cc5cb2
commit
b634f6b200
|
@ -14,6 +14,7 @@ import {
|
||||||
isNarrowAspectRatio,
|
isNarrowAspectRatio,
|
||||||
makeAspectRatioAware
|
makeAspectRatioAware
|
||||||
} from '../../base/responsive-ui';
|
} from '../../base/responsive-ui';
|
||||||
|
import { ColorPalette } from '../../base/styles';
|
||||||
import { InviteButton } from '../../invite';
|
import { InviteButton } from '../../invite';
|
||||||
import {
|
import {
|
||||||
EnterPictureInPictureToolbarButton
|
EnterPictureInPictureToolbarButton
|
||||||
|
@ -159,6 +160,11 @@ class Toolbox extends Component<Props> {
|
||||||
_renderPrimaryToolbar() {
|
_renderPrimaryToolbar() {
|
||||||
const audioButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.AUDIO);
|
const audioButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.AUDIO);
|
||||||
const videoButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.VIDEO);
|
const videoButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.VIDEO);
|
||||||
|
const hangupButtonStyles = {
|
||||||
|
iconStyle: styles.whitePrimaryToolbarButtonIcon,
|
||||||
|
style: styles.hangup,
|
||||||
|
underlayColor: ColorPalette.buttonUnderlay
|
||||||
|
};
|
||||||
|
|
||||||
/* eslint-disable react/jsx-handler-names */
|
/* eslint-disable react/jsx-handler-names */
|
||||||
|
|
||||||
|
@ -168,7 +174,7 @@ class Toolbox extends Component<Props> {
|
||||||
pointerEvents = 'box-none'
|
pointerEvents = 'box-none'
|
||||||
style = { styles.primaryToolbar }>
|
style = { styles.primaryToolbar }>
|
||||||
<AudioMuteButton styles = { audioButtonStyles } />
|
<AudioMuteButton styles = { audioButtonStyles } />
|
||||||
<HangupButton />
|
<HangupButton styles = { hangupButtonStyles } />
|
||||||
<VideoMuteButton styles = { videoButtonStyles } />
|
<VideoMuteButton styles = { videoButtonStyles } />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -354,12 +354,12 @@ class Toolbox extends Component<Props, State> {
|
||||||
</div> }
|
</div> }
|
||||||
</div>
|
</div>
|
||||||
<div className = 'button-group-center'>
|
<div className = 'button-group-center'>
|
||||||
{ this._shouldShowButton('microphone')
|
<AudioMuteButton
|
||||||
&& <AudioMuteButton /> }
|
visible = { this._shouldShowButton('microphone') } />
|
||||||
{ this._shouldShowButton('hangup')
|
<HangupButton
|
||||||
&& <HangupButton /> }
|
visible = { this._shouldShowButton('hangup') } />
|
||||||
{ this._shouldShowButton('camera')
|
<VideoMuteButton
|
||||||
&& <VideoMuteButton /> }
|
visible = { this._shouldShowButton('camera') } />
|
||||||
</div>
|
</div>
|
||||||
<div className = 'button-group-right'>
|
<div className = 'button-group-right'>
|
||||||
{ this._shouldShowButton('invite')
|
{ this._shouldShowButton('invite')
|
||||||
|
|
|
@ -1,87 +1,62 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
import AbstractButton from './AbstractButton';
|
||||||
import { Component } from 'react';
|
import type { Props } from './AbstractButton';
|
||||||
|
|
||||||
import {
|
|
||||||
AUDIO_MUTE,
|
|
||||||
createToolbarEvent,
|
|
||||||
sendAnalytics
|
|
||||||
} from '../../../analytics';
|
|
||||||
import {
|
|
||||||
VIDEO_MUTISM_AUTHORITY,
|
|
||||||
setAudioMuted
|
|
||||||
} from '../../../base/media';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract implementation of a button for toggling audio mute.
|
* An abstract implementation of a button for toggling audio mute.
|
||||||
*/
|
*/
|
||||||
export default class AbstractAudioMuteButton extends Component<*> {
|
class AbstractAudioMuteButton<P: Props, S: *> extends AbstractButton<P, S> {
|
||||||
/**
|
accessibilityLabel = 'Audio mute';
|
||||||
* {@code AbstractAudioMuteButton} component's property types.
|
iconName = 'icon-microphone';
|
||||||
*
|
toggledIconName = 'icon-mic-disabled toggled';
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
/**
|
|
||||||
* Whether or not the local microphone is muted.
|
|
||||||
*/
|
|
||||||
_audioMuted: PropTypes.bool,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked to toggle audio mute.
|
|
||||||
*/
|
|
||||||
dispatch: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new {@code AbstractAudioMuteButton} instance.
|
* Handles clicking / pressing the button, and toggles the audio mute state
|
||||||
*
|
* accordingly.
|
||||||
* @param {Props} props - The read-only React {@code Component} props with
|
|
||||||
* which the new instance is to be initialized.
|
|
||||||
*/
|
|
||||||
constructor(props: Object) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Bind event handler so it is only bound once per instance.
|
|
||||||
this._onToolbarToggleAudio = this._onToolbarToggleAudio.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches an action to toggle audio mute.
|
|
||||||
*
|
*
|
||||||
|
* @override
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_doToggleAudio() {
|
_handleClick() {
|
||||||
// The user sees the reality i.e. the state of base/tracks and intends
|
this._setAudioMuted(!this._isAudioMuted());
|
||||||
// to change reality by tapping on the respective button i.e. the user
|
|
||||||
// sets the state of base/media. Whether the user's intention will turn
|
|
||||||
// into reality is a whole different story which is of no concern to the
|
|
||||||
// tapping.
|
|
||||||
this.props.dispatch(
|
|
||||||
setAudioMuted(
|
|
||||||
!this.props._audioMuted,
|
|
||||||
VIDEO_MUTISM_AUTHORITY.USER,
|
|
||||||
/* ensureTrack */ true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onToolbarToggleAudio: () => void;
|
/**
|
||||||
|
* Helper function to be implemented by subclasses, which must return a
|
||||||
|
* boolean value indicating if audio is muted or not.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isAudioMuted() {
|
||||||
|
// To be implemented by subclass.
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an analytics toolbar event and dispatches an action for toggling
|
* Indicates whether this button is in toggled state or not.
|
||||||
* audio mute.
|
|
||||||
*
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isToggled() {
|
||||||
|
return this._isAudioMuted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to perform the actual setting of the audio mute / unmute
|
||||||
|
* action.
|
||||||
|
*
|
||||||
|
* @param {boolean} audioMuted - Whether video should be muted or not.
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onToolbarToggleAudio() {
|
_setAudioMuted(audioMuted: boolean) { // eslint-disable-line no-unused-vars
|
||||||
sendAnalytics(createToolbarEvent(
|
// To be implemented by subclass.
|
||||||
AUDIO_MUTE,
|
|
||||||
{
|
|
||||||
enable: !this.props._audioMuted
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._doToggleAudio();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default AbstractAudioMuteButton;
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import ToolboxItem from '../ToolboxItem';
|
||||||
|
import type { Styles } from '../AbstractToolboxItem';
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show the label or not.
|
||||||
|
*/
|
||||||
|
showLabel: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of styles for the button.
|
||||||
|
*/
|
||||||
|
styles: ?Styles,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of styles for the button, when in toggled state.
|
||||||
|
*/
|
||||||
|
toggledStyles: ?Styles,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From which direction the tooltip should appear, relative to the
|
||||||
|
* button.
|
||||||
|
*/
|
||||||
|
tooltipPosition: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this button is visible or not.
|
||||||
|
*/
|
||||||
|
visible: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract implementation of a button.
|
||||||
|
*/
|
||||||
|
export default class AbstractButton<P: Props, S : *> extends Component<P, S> {
|
||||||
|
static defaultProps = {
|
||||||
|
showLabel: false,
|
||||||
|
styles: undefined,
|
||||||
|
toggledStyles: undefined,
|
||||||
|
tooltipPosition: 'top',
|
||||||
|
visible: true
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A succinct description of what the button does. Used by accessibility
|
||||||
|
* tools and torture tests.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
accessibilityLabel: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the icon of this button.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
iconName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text associated with this button. When `showLabel` is set to
|
||||||
|
* {@code true}, it will be displayed alongside the icon.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
label: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the icon of this button, when toggled.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
toggledIconName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text to display in the tooltip. Used only on web.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
tooltip: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new {@code AbstractButton} instance.
|
||||||
|
*
|
||||||
|
* @param {Props} props - The React {@code Component} props to initialize
|
||||||
|
* the new {@code AbstractAudioMuteButton} instance with.
|
||||||
|
*/
|
||||||
|
constructor(props: P) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._onClick = this._onClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to be implemented by subclasses, which should be used
|
||||||
|
* to handle the button being clicked / pressed.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClick() {
|
||||||
|
// To be implemented by subclass.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current icon name, taking the toggled state into account. If no
|
||||||
|
* toggled icon is provided, the regular icon will also be used in the
|
||||||
|
* toggled state.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
_getIconName() {
|
||||||
|
return (this._isToggled() ? this.toggledIconName : this.iconName)
|
||||||
|
|| this.iconName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current styles, taking the toggled state into account. If no
|
||||||
|
* toggled styles are provided, the regular styles will also be used in the
|
||||||
|
* toggled state.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {?Styles}
|
||||||
|
*/
|
||||||
|
_getStyles() {
|
||||||
|
const { styles, toggledStyles } = this.props;
|
||||||
|
|
||||||
|
return (this._isToggled() ? toggledStyles : styles) || styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to be implemented by subclasses, which must return a
|
||||||
|
* boolean value indicating if this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to be implemented by subclasses, which must return a
|
||||||
|
* boolean value indicating if this button is toggled or not.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isToggled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onClick: (*) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles clicking / pressing the button, and toggles the audio mute state
|
||||||
|
* accordingly.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onClick() {
|
||||||
|
this._handleClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const props = {
|
||||||
|
...this.props,
|
||||||
|
accessibilityLabel: this.accessibilityLabel,
|
||||||
|
iconName: this._getIconName(),
|
||||||
|
label: this.label,
|
||||||
|
styles: this._getStyles(),
|
||||||
|
tooltip: this.tooltip
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolboxItem
|
||||||
|
disabled = { this._isDisabled() }
|
||||||
|
onClick = { this._onClick }
|
||||||
|
{ ...props } />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,51 +1,35 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { Component } from 'react';
|
import AbstractButton from './AbstractButton';
|
||||||
|
import type { Props } from './AbstractButton';
|
||||||
import {
|
|
||||||
createToolbarEvent,
|
|
||||||
sendAnalytics
|
|
||||||
} from '../../../analytics';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract implementation of a button for leaving the conference.
|
* An abstract implementation of a button for disconnecting a conference.
|
||||||
*/
|
*/
|
||||||
export default class AbstractHangupButton extends Component<*> {
|
class AbstractHangupButton<P : Props, S: *> extends AbstractButton<P, S> {
|
||||||
/**
|
accessibilityLabel = 'Hangup';
|
||||||
* Initializes a new {@code AbstractHangupButton} instance.
|
iconName = 'icon-hangup';
|
||||||
*
|
|
||||||
* @param {Props} props - The read-only React {@code Component} props with
|
|
||||||
* which the new instance is to be initialized.
|
|
||||||
*/
|
|
||||||
constructor(props: Object) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Bind event handler so it is only bound once per instance.
|
/**
|
||||||
this._onToolbarHangup = this._onToolbarHangup.bind(this);
|
* Handles clicking / pressing the button, and disconnects the conference.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClick() {
|
||||||
|
this._doHangup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches an action for leaving the current conference.
|
* Helper function to perform the actual hangup action.
|
||||||
*
|
*
|
||||||
|
* @abstract
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_doHangup() {
|
_doHangup() {
|
||||||
/* to be implemented by descendants */
|
// To be implemented by subclass.
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarHangup: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an analytics toolbar event and dispatches an action for leaving
|
|
||||||
* the current conference.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onToolbarHangup() {
|
|
||||||
sendAnalytics(createToolbarEvent('hangup'));
|
|
||||||
|
|
||||||
this._doHangup();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default AbstractHangupButton;
|
||||||
|
|
|
@ -1,88 +1,61 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
import AbstractButton from './AbstractButton';
|
||||||
import { Component } from 'react';
|
import type { Props } from './AbstractButton';
|
||||||
|
|
||||||
import {
|
|
||||||
VIDEO_MUTE,
|
|
||||||
createToolbarEvent,
|
|
||||||
sendAnalytics
|
|
||||||
} from '../../../analytics';
|
|
||||||
import {
|
|
||||||
VIDEO_MUTISM_AUTHORITY,
|
|
||||||
setVideoMuted
|
|
||||||
} from '../../../base/media';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract implementation of a button for toggling video mute.
|
* An abstract implementation of a button for toggling video mute.
|
||||||
*/
|
*/
|
||||||
export default class AbstractVideoMuteButton extends Component<*> {
|
class AbstractVideoMuteButton<P : Props, S : *> extends AbstractButton<P, S> {
|
||||||
/**
|
accessibilityLabel = 'Video mute';
|
||||||
* {@code AbstractVideoMuteButton} component's property types.
|
iconName = 'icon-camera';
|
||||||
*
|
toggledIconName = 'icon-camera-disabled toggled';
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
/**
|
|
||||||
* Whether or not the local camera is muted.
|
|
||||||
*/
|
|
||||||
_videoMuted: PropTypes.bool,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked to toggle video mute.
|
|
||||||
*/
|
|
||||||
dispatch: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new {@code AbstractVideoMuteButton} instance.
|
* Handles clicking / pressing the button, and toggles the video mute state
|
||||||
*
|
* accordingly.
|
||||||
* @param {Props} props - The read-only React {@code Component} props with
|
|
||||||
* which the new instance is to be initialized.
|
|
||||||
*/
|
|
||||||
constructor(props: Object) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Bind event handler so it is only bound once per instance.
|
|
||||||
this._onToolbarToggleVideo = this._onToolbarToggleVideo.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches an action to toggle the mute state of the video/camera.
|
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_doToggleVideo() {
|
_handleClick() {
|
||||||
// The user sees the reality i.e. the state of base/tracks and intends
|
this._setVideoMuted(!this._isVideoMuted());
|
||||||
// to change reality by tapping on the respective button i.e. the user
|
|
||||||
// sets the state of base/media. Whether the user's intention will turn
|
|
||||||
// into reality is a whole different story which is of no concern to the
|
|
||||||
// tapping.
|
|
||||||
this.props.dispatch(
|
|
||||||
setVideoMuted(
|
|
||||||
!this.props._videoMuted,
|
|
||||||
VIDEO_MUTISM_AUTHORITY.USER,
|
|
||||||
/* ensureTrack */ true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
_onToolbarToggleVideo: () => void;
|
* Indicates whether this button is in toggled state or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isToggled() {
|
||||||
|
return this._isVideoMuted();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an analytics toolbar event and dispatches an action for toggling
|
* Helper function to be implemented by subclasses, which must return a
|
||||||
* video mute.
|
* boolean value indicating if video is muted or not.
|
||||||
*
|
*
|
||||||
|
* @abstract
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isVideoMuted() {
|
||||||
|
// To be implemented by subclass.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to perform the actual setting of the video mute / unmute
|
||||||
|
* action.
|
||||||
|
*
|
||||||
|
* @param {boolean} videoMuted - Whether video should be muted or not.
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onToolbarToggleVideo() {
|
_setVideoMuted(videoMuted: boolean) { // eslint-disable-line no-unused-vars
|
||||||
sendAnalytics(createToolbarEvent(
|
// To be implemented by subclass.
|
||||||
VIDEO_MUTE,
|
|
||||||
{
|
|
||||||
enable: !this.props._videoMuted
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._doToggleVideo();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default AbstractVideoMuteButton;
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AUDIO_MUTE,
|
||||||
|
createToolbarEvent,
|
||||||
|
sendAnalytics
|
||||||
|
} from '../../../analytics';
|
||||||
|
import { translate } from '../../../base/i18n';
|
||||||
|
import {
|
||||||
|
MEDIA_TYPE,
|
||||||
|
setAudioMuted
|
||||||
|
} from '../../../base/media';
|
||||||
|
import { isLocalTrackMuted } from '../../../base/tracks';
|
||||||
|
|
||||||
|
import AbstractAudioMuteButton from './AbstractAudioMuteButton';
|
||||||
|
import type { Props as AbstractButtonProps } from './AbstractButton';
|
||||||
|
|
||||||
|
type Props = AbstractButtonProps & {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether audio is currently muted or not.
|
||||||
|
*/
|
||||||
|
_audioMuted: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The redux {@code dispatch} function.
|
||||||
|
*/
|
||||||
|
dispatch: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders a toolbar button for toggling audio mute.
|
||||||
|
*
|
||||||
|
* @extends AbstractAudioMuteButton
|
||||||
|
*/
|
||||||
|
class AudioMuteButton extends AbstractAudioMuteButton<Props, *> {
|
||||||
|
label = 'toolbar.mute';
|
||||||
|
tooltip = 'toolbar.mute';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this button should be disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if audio is currently muted ot nor.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isAudioMuted() {
|
||||||
|
return this.props._audioMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the muted state.
|
||||||
|
*
|
||||||
|
* @param {boolean} audioMuted - Whether audio should be muted or not.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_setAudioMuted(audioMuted: boolean) {
|
||||||
|
sendAnalytics(createToolbarEvent(AUDIO_MUTE, { enable: audioMuted }));
|
||||||
|
this.props.dispatch(setAudioMuted(audioMuted));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps (parts of) the redux state to the associated props for the
|
||||||
|
* {@code AudioMuteButton} component.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The Redux state.
|
||||||
|
* @private
|
||||||
|
* @returns {{
|
||||||
|
* _audioMuted: boolean
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
function _mapStateToProps(state): Object {
|
||||||
|
const tracks = state['features/base/tracks'];
|
||||||
|
|
||||||
|
return {
|
||||||
|
_audioMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(connect(_mapStateToProps)(AudioMuteButton));
|
|
@ -1,73 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { MEDIA_TYPE } from '../../../base/media';
|
|
||||||
import { isLocalTrackMuted } from '../../../base/tracks';
|
|
||||||
|
|
||||||
import AbstractAudioMuteButton from './AbstractAudioMuteButton';
|
|
||||||
import ToolbarButton from '../ToolbarButton';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component that renders a toolbar button for toggling audio mute.
|
|
||||||
*
|
|
||||||
* @extends AbstractAudioMuteButton
|
|
||||||
*/
|
|
||||||
export class AudioMuteButton extends AbstractAudioMuteButton {
|
|
||||||
/**
|
|
||||||
* {@code AbstractAudioMuteButton} component's property types.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
...AbstractAudioMuteButton.propTypes,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styles to be applied to the button and the icon to show.
|
|
||||||
*/
|
|
||||||
buttonStyles: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const { buttonStyles } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToolbarButton
|
|
||||||
iconName = { buttonStyles.iconName }
|
|
||||||
iconStyle = { buttonStyles.iconStyle }
|
|
||||||
onClick = { this._onToolbarToggleAudio }
|
|
||||||
style = { buttonStyles.style } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarToggleAudio: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps (parts of) the Redux state to the associated props for the
|
|
||||||
* {@code AudioMuteButton} component.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state.
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* _audioMuted: boolean,
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state) {
|
|
||||||
const tracks = state['features/base/tracks'];
|
|
||||||
|
|
||||||
return {
|
|
||||||
_audioMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default connect(_mapStateToProps)(AudioMuteButton);
|
|
|
@ -1,181 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import UIEvents from '../../../../../service/UI/UIEvents';
|
|
||||||
import {
|
|
||||||
ACTION_SHORTCUT_TRIGGERED,
|
|
||||||
AUDIO_MUTE,
|
|
||||||
createShortcutEvent,
|
|
||||||
sendAnalytics
|
|
||||||
} from '../../../analytics';
|
|
||||||
import { translate } from '../../../base/i18n';
|
|
||||||
import { MEDIA_TYPE } from '../../../base/media';
|
|
||||||
import { isLocalTrackMuted } from '../../../base/tracks';
|
|
||||||
|
|
||||||
import AbstractAudioMuteButton from './AbstractAudioMuteButton';
|
|
||||||
import ToolbarButton from '../ToolbarButton';
|
|
||||||
|
|
||||||
declare var APP: Object;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component that renders a toolbar button for toggling audio mute.
|
|
||||||
*
|
|
||||||
* @extends Component
|
|
||||||
*/
|
|
||||||
export class AudioMuteButton extends AbstractAudioMuteButton {
|
|
||||||
/**
|
|
||||||
* Default values for {@code AudioMuteButton} component's properties.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static defaultProps = {
|
|
||||||
tooltipPosition: 'top'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code AudioMuteButton} component's property types.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
...AbstractAudioMuteButton.propTypes,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@code JitsiConference} for the current conference.
|
|
||||||
*/
|
|
||||||
_conference: PropTypes.object,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked to update the audio mute status.
|
|
||||||
*/
|
|
||||||
dispatch: PropTypes.func,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked to obtain translated strings.
|
|
||||||
*/
|
|
||||||
t: PropTypes.func,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where the tooltip should display, relative to the button.
|
|
||||||
*/
|
|
||||||
tooltipPosition: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a new {@code AudioMuteButton} instance.
|
|
||||||
*
|
|
||||||
* @param {Props} props - The read-only React {@code Component} props with
|
|
||||||
* which the new instance is to be initialized.
|
|
||||||
*/
|
|
||||||
constructor(props: Object) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Bind event handlers so it is only bound once per instance.
|
|
||||||
this._onShortcutToggleAudio = this._onShortcutToggleAudio.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a keyboard shortcuts for toggling audio mute.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
componentDidMount() {
|
|
||||||
APP.keyboardshortcut.registerShortcut(
|
|
||||||
'M',
|
|
||||||
null,
|
|
||||||
this._onShortcutToggleAudio,
|
|
||||||
'keyboardShortcuts.mute');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the registered keyboard shortcut handler.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
componentWillUnmount() {
|
|
||||||
APP.keyboardshortcut.unregisterShortcut('M');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const { _audioMuted, _conference, t, tooltipPosition } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToolbarButton
|
|
||||||
accessibilityLabel = 'Audio mute'
|
|
||||||
iconName = { _audioMuted && _conference
|
|
||||||
? 'icon-mic-disabled toggled'
|
|
||||||
: 'icon-microphone' }
|
|
||||||
onClick = { this._onToolbarToggleAudio }
|
|
||||||
tooltip = { t('toolbar.mute') }
|
|
||||||
tooltipPosition = { tooltipPosition } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_doToggleAudio: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits an event to signal audio mute should be toggled.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_doToggleAudio() {
|
|
||||||
// The old conference logic must be used for now as the redux flows do
|
|
||||||
// not handle all cases, such as unmuting when the config
|
|
||||||
// startWithAudioMuted is true.
|
|
||||||
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, !this.props._audioMuted, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onShortcutToggleAudio: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an analytics keyboard shortcut event and dispatches an action for
|
|
||||||
* toggling audio mute.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onShortcutToggleAudio() {
|
|
||||||
sendAnalytics(createShortcutEvent(
|
|
||||||
AUDIO_MUTE,
|
|
||||||
ACTION_SHORTCUT_TRIGGERED,
|
|
||||||
{ enable: !this.props._audioMuted }));
|
|
||||||
|
|
||||||
this._doToggleAudio();
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarToggleAudio: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps (parts of) the Redux state to the associated props for the
|
|
||||||
* {@code AudioMuteButton} component.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state.
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* _audioMuted: boolean,
|
|
||||||
* _conference: Object,
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state) {
|
|
||||||
const tracks = state['features/base/tracks'];
|
|
||||||
|
|
||||||
return {
|
|
||||||
_audioMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO),
|
|
||||||
_conference: state['features/base/conference'].conference
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps)(AudioMuteButton));
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { createToolbarEvent, sendAnalytics } from '../../../analytics';
|
||||||
|
import { appNavigate } from '../../../app';
|
||||||
|
|
||||||
|
import { disconnect } from '../../../base/connection';
|
||||||
|
import { translate } from '../../../base/i18n';
|
||||||
|
|
||||||
|
import AbstractHangupButton from './AbstractHangupButton';
|
||||||
|
import type { Props as AbstractButtonProps } from './AbstractButton';
|
||||||
|
|
||||||
|
type Props = AbstractButtonProps & {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The redux {@code dispatch} function.
|
||||||
|
*/
|
||||||
|
dispatch: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders a toolbar button for leaving the current conference.
|
||||||
|
*
|
||||||
|
* @extends AbstractHangupButton
|
||||||
|
*/
|
||||||
|
class HangupButton extends AbstractHangupButton<Props, *> {
|
||||||
|
label = 'toolbar.hangup';
|
||||||
|
tooltip = 'toolbar.hangup';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to perform the actual hangup action.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_doHangup() {
|
||||||
|
sendAnalytics(createToolbarEvent('hangup'));
|
||||||
|
|
||||||
|
// FIXME: these should be unified.
|
||||||
|
if (navigator.product === 'ReactNative') {
|
||||||
|
this.props.dispatch(appNavigate(undefined));
|
||||||
|
} else {
|
||||||
|
this.props.dispatch(disconnect(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this button should be disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(connect()(HangupButton));
|
|
@ -1,63 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { appNavigate } from '../../../app';
|
|
||||||
import { ColorPalette } from '../../../base/styles';
|
|
||||||
|
|
||||||
import AbstractHangupButton from './AbstractHangupButton';
|
|
||||||
import ToolbarButton from '../ToolbarButton';
|
|
||||||
import styles from '../styles';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component that renders a toolbar button for leaving the current conference.
|
|
||||||
*
|
|
||||||
* @extends Component
|
|
||||||
*/
|
|
||||||
class HangupButton extends AbstractHangupButton {
|
|
||||||
/**
|
|
||||||
* {@code HangupButton} component's property types.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
/**
|
|
||||||
* Invoked to leave the conference.
|
|
||||||
*/
|
|
||||||
dispatch: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<ToolbarButton
|
|
||||||
accessibilityLabel = 'Hangup'
|
|
||||||
iconName = 'hangup'
|
|
||||||
iconStyle = { styles.whitePrimaryToolbarButtonIcon }
|
|
||||||
onClick = { this._onToolbarHangup }
|
|
||||||
style = { styles.hangup }
|
|
||||||
underlayColor = { ColorPalette.buttonUnderlay } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches an action for leaving the current conference.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_doHangup() {
|
|
||||||
this.props.dispatch(appNavigate(undefined));
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarHangup: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect()(HangupButton);
|
|
|
@ -1,83 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { disconnect } from '../../../base/connection';
|
|
||||||
import { translate } from '../../../base/i18n';
|
|
||||||
|
|
||||||
import AbstractHangupButton from './AbstractHangupButton';
|
|
||||||
import ToolbarButton from '../ToolbarButton';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component that renders a toolbar button for leaving the current conference.
|
|
||||||
*
|
|
||||||
* @extends Component
|
|
||||||
*/
|
|
||||||
export class HangupButton extends AbstractHangupButton {
|
|
||||||
/**
|
|
||||||
* Default values for {@code HangupButton} component's properties.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static defaultProps = {
|
|
||||||
tooltipPosition: 'top'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code HangupButton} component's property types.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
/**
|
|
||||||
* Invoked to trigger conference leave.
|
|
||||||
*/
|
|
||||||
dispatch: PropTypes.func,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked to obtain translated strings.
|
|
||||||
*/
|
|
||||||
t: PropTypes.func,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where the tooltip should display, relative to the button.
|
|
||||||
*/
|
|
||||||
tooltipPosition: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const { t, tooltipPosition } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToolbarButton
|
|
||||||
accessibilityLabel = 'Hangup'
|
|
||||||
iconName = 'icon-hangup'
|
|
||||||
onClick = { this._onToolbarHangup }
|
|
||||||
tooltip = { t('toolbar.hangup') }
|
|
||||||
tooltipPosition = { tooltipPosition } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarHangup: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches an action for leaving the current conference.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_doHangup() {
|
|
||||||
this.props.dispatch(disconnect(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate(connect()(HangupButton));
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import {
|
||||||
|
VIDEO_MUTE,
|
||||||
|
createToolbarEvent,
|
||||||
|
sendAnalytics
|
||||||
|
} from '../../../analytics';
|
||||||
|
import { translate } from '../../../base/i18n';
|
||||||
|
import {
|
||||||
|
MEDIA_TYPE,
|
||||||
|
VIDEO_MUTISM_AUTHORITY,
|
||||||
|
setVideoMuted
|
||||||
|
} from '../../../base/media';
|
||||||
|
import { isLocalTrackMuted } from '../../../base/tracks';
|
||||||
|
|
||||||
|
import AbstractVideoMuteButton from './AbstractVideoMuteButton';
|
||||||
|
import type { Props as AbstractButtonProps } from './AbstractButton';
|
||||||
|
|
||||||
|
type Props = AbstractButtonProps & {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current conference is in audio only mode or not.
|
||||||
|
*/
|
||||||
|
_audioOnly: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether video is currently muted or not.
|
||||||
|
*/
|
||||||
|
_videoMuted: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The redux {@code dispatch} function.
|
||||||
|
*/
|
||||||
|
dispatch: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders a toolbar button for toggling video mute.
|
||||||
|
*
|
||||||
|
* @extends AbstractVideoMuteButton
|
||||||
|
*/
|
||||||
|
class VideoMuteButton extends AbstractVideoMuteButton<Props, *> {
|
||||||
|
label = 'toolbar.videomute';
|
||||||
|
tooltip = 'toolbar.videomute';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this button should be disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return this.props._audioOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if video is currently muted ot nor.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isVideoMuted() {
|
||||||
|
return this.props._videoMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the muted state.
|
||||||
|
*
|
||||||
|
* @param {boolean} videoMuted - Whether video should be muted or not.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_setVideoMuted(videoMuted: boolean) {
|
||||||
|
sendAnalytics(createToolbarEvent(VIDEO_MUTE, { enable: videoMuted }));
|
||||||
|
this.props.dispatch(
|
||||||
|
setVideoMuted(
|
||||||
|
videoMuted,
|
||||||
|
VIDEO_MUTISM_AUTHORITY.USER,
|
||||||
|
/* ensureTrack */ true));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps (parts of) the redux state to the associated props for the
|
||||||
|
* {@code VideoMuteButton} component.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The Redux state.
|
||||||
|
* @private
|
||||||
|
* @returns {{
|
||||||
|
* _audioOnly: boolean,
|
||||||
|
* _videoMuted: boolean
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
function _mapStateToProps(state): Object {
|
||||||
|
const { audioOnly } = state['features/base/conference'];
|
||||||
|
const tracks = state['features/base/tracks'];
|
||||||
|
|
||||||
|
return {
|
||||||
|
_audioOnly: Boolean(audioOnly),
|
||||||
|
_videoMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(connect(_mapStateToProps)(VideoMuteButton));
|
|
@ -1,82 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { MEDIA_TYPE } from '../../../base/media';
|
|
||||||
import { isLocalTrackMuted } from '../../../base/tracks';
|
|
||||||
|
|
||||||
import AbstractVideoMuteButton from './AbstractVideoMuteButton';
|
|
||||||
import ToolbarButton from '../ToolbarButton';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component that renders a toolbar button for toggling video mute.
|
|
||||||
*
|
|
||||||
* @extends AbstractVideoMuteButton
|
|
||||||
*/
|
|
||||||
class VideoMuteButton extends AbstractVideoMuteButton {
|
|
||||||
/**
|
|
||||||
* {@code VideoMuteButton} component's property types.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
...AbstractVideoMuteButton.propTypes,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the local participant is current in audio only mode.
|
|
||||||
* Video mute toggling is disabled in audio only mode.
|
|
||||||
*/
|
|
||||||
_audioOnly: PropTypes.bool,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styles to be applied to the button and the icon to show.
|
|
||||||
*/
|
|
||||||
buttonStyles: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const { _audioOnly, buttonStyles } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToolbarButton
|
|
||||||
disabled = { _audioOnly }
|
|
||||||
iconName = { buttonStyles.iconName }
|
|
||||||
iconStyle = { buttonStyles.iconStyle }
|
|
||||||
onClick = { this._onToolbarToggleVideo }
|
|
||||||
style = { buttonStyles.style } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarToggleVideo: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps (parts of) the Redux state to the associated props for the
|
|
||||||
* {@code VideoMuteButton} component.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state.
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* _audioOnly: boolean,
|
|
||||||
* _videoMuted: boolean
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state) {
|
|
||||||
const conference = state['features/base/conference'];
|
|
||||||
const tracks = state['features/base/tracks'];
|
|
||||||
|
|
||||||
return {
|
|
||||||
_audioOnly: Boolean(conference.audioOnly),
|
|
||||||
_videoMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(_mapStateToProps)(VideoMuteButton);
|
|
|
@ -1,173 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import UIEvents from '../../../../../service/UI/UIEvents';
|
|
||||||
import {
|
|
||||||
ACTION_SHORTCUT_TRIGGERED,
|
|
||||||
VIDEO_MUTE,
|
|
||||||
createShortcutEvent,
|
|
||||||
sendAnalytics
|
|
||||||
} from '../../../analytics';
|
|
||||||
import { translate } from '../../../base/i18n';
|
|
||||||
import { MEDIA_TYPE } from '../../../base/media';
|
|
||||||
import { isLocalTrackMuted } from '../../../base/tracks';
|
|
||||||
|
|
||||||
import AbstractVideoMuteButton from './AbstractVideoMuteButton';
|
|
||||||
import ToolbarButton from '../ToolbarButton';
|
|
||||||
|
|
||||||
declare var APP: Object;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component that renders a toolbar button for toggling video mute.
|
|
||||||
*
|
|
||||||
* @extends AbstractVideoMuteButton
|
|
||||||
*/
|
|
||||||
export class VideoMuteButton extends AbstractVideoMuteButton {
|
|
||||||
/**
|
|
||||||
* Default values for {@code VideoMuteButton} component's properties.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static defaultProps = {
|
|
||||||
tooltipPosition: 'top'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code VideoMuteButton} component's property types.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
...AbstractVideoMuteButton.propTypes,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@code JitsiConference} for the current conference.
|
|
||||||
*/
|
|
||||||
_conference: PropTypes.object,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked to obtain translated strings.
|
|
||||||
*/
|
|
||||||
t: PropTypes.func,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where the tooltip should display, relative to the button.
|
|
||||||
*/
|
|
||||||
tooltipPosition: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a new {@code VideoMuteButton} instance.
|
|
||||||
*
|
|
||||||
* @param {Props} props - The read-only React {@code Component} props with
|
|
||||||
* which the new instance is to be initialized.
|
|
||||||
*/
|
|
||||||
constructor(props: Object) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once per instance.
|
|
||||||
this._onShortcutToggleVideo = this._onShortcutToggleVideo.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a keyboard shortcuts for toggling video mute.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
componentDidMount() {
|
|
||||||
APP.keyboardshortcut.registerShortcut(
|
|
||||||
'V',
|
|
||||||
null,
|
|
||||||
this._onShortcutToggleVideo,
|
|
||||||
'keyboardShortcuts.videoMute');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the registered keyboard shortcut handler.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
componentWillUnmount() {
|
|
||||||
APP.keyboardshortcut.unregisterShortcut('V');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const { _conference, _videoMuted, t, tooltipPosition } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToolbarButton
|
|
||||||
accessibilityLabel = 'Video mute'
|
|
||||||
iconName = { _videoMuted && _conference
|
|
||||||
? 'icon-camera-disabled toggled'
|
|
||||||
: 'icon-camera' }
|
|
||||||
onClick = { this._onToolbarToggleVideo }
|
|
||||||
tooltip = { t('toolbar.videomute') }
|
|
||||||
tooltipPosition = { tooltipPosition } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_doToggleVideo: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits an event to signal video mute should be toggled.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_doToggleVideo() {
|
|
||||||
APP.UI.emitEvent(UIEvents.VIDEO_MUTED, !this.props._videoMuted);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onShortcutToggleVideo: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an analytics keyboard shortcut event for and dispatches an action
|
|
||||||
* for toggling video mute.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onShortcutToggleVideo() {
|
|
||||||
sendAnalytics(createShortcutEvent(
|
|
||||||
VIDEO_MUTE,
|
|
||||||
ACTION_SHORTCUT_TRIGGERED,
|
|
||||||
{ enable: !this.props._videoMuted }));
|
|
||||||
|
|
||||||
this._doToggleVideo();
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarToggleVideo: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps (parts of) the Redux state to the associated props for the
|
|
||||||
* {@code AudioMuteButton} component.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state.
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* _conference: Object,
|
|
||||||
* _videoMuted: boolean,
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state) {
|
|
||||||
const tracks = state['features/base/tracks'];
|
|
||||||
|
|
||||||
return {
|
|
||||||
_conference: state['features/base/conference'].conference,
|
|
||||||
_videoMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps)(VideoMuteButton));
|
|
Loading…
Reference in New Issue