diff --git a/react/features/base/config/constants.js b/react/features/base/config/constants.js index 5600265b3..1922c1ff0 100644 --- a/react/features/base/config/constants.js +++ b/react/features/base/config/constants.js @@ -18,6 +18,6 @@ export const TOOLBAR_BUTTONS = [ 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording', 'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand', 'videoquality', 'filmstrip', 'participants-pane', 'feedback', 'stats', 'shortcuts', - 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', + 'tileview', 'toggle-camera', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security' ]; diff --git a/react/features/toolbox/components/web/ToggleCameraButton.js b/react/features/toolbox/components/web/ToggleCameraButton.js new file mode 100644 index 000000000..60c2a0792 --- /dev/null +++ b/react/features/toolbox/components/web/ToggleCameraButton.js @@ -0,0 +1,75 @@ +// @flow + +import { translate } from '../../../base/i18n'; +import { IconCameraRefresh } from '../../../base/icons'; +import { connect } from '../../../base/redux'; +import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components'; +import { isLocalCameraTrackMuted, isToggleCameraEnabled, toggleCamera } from '../../../base/tracks'; + +/** + * The type of the React {@code Component} props of {@link ToggleCameraButton}. + */ +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 dispatch function. + */ + dispatch: Function +}; + +/** + * An implementation of a button for toggling the camera facing mode. + */ +class ToggleCameraButton extends AbstractButton { + accessibilityLabel = 'toolbar.accessibilityLabel.toggleCamera'; + icon = IconCameraRefresh; + label = 'toolbar.toggleCamera'; + + /** + * Handles clicking/pressing the button. + * + * @returns {void} + */ + _handleClick() { + this.props.dispatch(toggleCamera()); + } + + /** + * Whether this button is disabled or not. + * + * @returns {boolean} + */ + _isDisabled() { + return this.props._audioOnly || this.props._videoMuted; + } +} + +/** + * Maps (parts of) the redux state to the associated props for the + * {@code ToggleCameraButton} component. + * + * @param {Object} state - The Redux state. + * @returns {Props} + */ +function mapStateToProps(state): Object { + const { enabled: audioOnly } = state['features/base/audio-only']; + const tracks = state['features/base/tracks']; + + return { + _audioOnly: Boolean(audioOnly), + _videoMuted: isLocalCameraTrackMuted(tracks), + visible: isToggleCameraEnabled(state) + }; +} + +export default translate(connect(mapStateToProps)(ToggleCameraButton)); diff --git a/react/features/toolbox/components/web/Toolbox.js b/react/features/toolbox/components/web/Toolbox.js index 8e25e9889..a289b70da 100644 --- a/react/features/toolbox/components/web/Toolbox.js +++ b/react/features/toolbox/components/web/Toolbox.js @@ -88,6 +88,7 @@ import ProfileButton from './ProfileButton'; import RaiseHandButton from './RaiseHandButton'; import Separator from './Separator'; import ShareDesktopButton from './ShareDesktopButton'; +import ToggleCameraButton from './ToggleCameraButton'; import VideoSettingsButton from './VideoSettingsButton'; /** @@ -609,6 +610,12 @@ class Toolbox extends Component { group: 2 }; + const toggleCamera = { + key: 'toggle-camera', + Content: ToggleCameraButton, + group: 2 + }; + const videoQuality = { key: 'videoquality', Content: VideoQualityButton, @@ -741,6 +748,7 @@ class Toolbox extends Component { raisehand, participants, tileview, + toggleCamera, videoQuality, fullscreen, security,