[RN] Refactor Toolbox
Create standalone components for each feature and move all state to them. Toolbars are now dummy containers.
This commit is contained in:
parent
450400b768
commit
a2834a2495
|
@ -74,6 +74,7 @@
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"addPeople": "Add people to your call",
|
"addPeople": "Add people to your call",
|
||||||
"audioonly": "Enable / Disable audio only mode (saves bandwidth)",
|
"audioonly": "Enable / Disable audio only mode (saves bandwidth)",
|
||||||
|
"audioRoute": "Select the audio route",
|
||||||
"callQuality": "Manage call quality",
|
"callQuality": "Manage call quality",
|
||||||
"enterFullScreen": "View full screen",
|
"enterFullScreen": "View full screen",
|
||||||
"exitFullScreen": "Exit full screen",
|
"exitFullScreen": "Exit full screen",
|
||||||
|
@ -87,6 +88,7 @@
|
||||||
"etherpad": "Open / Close shared document",
|
"etherpad": "Open / Close shared document",
|
||||||
"documentOpen": "Open shared document",
|
"documentOpen": "Open shared document",
|
||||||
"documentClose": "Close shared document",
|
"documentClose": "Close shared document",
|
||||||
|
"shareRoom": "Share room",
|
||||||
"sharedvideo": "Share a YouTube video",
|
"sharedvideo": "Share a YouTube video",
|
||||||
"stopSharedVideo": "Stop YouTube video",
|
"stopSharedVideo": "Stop YouTube video",
|
||||||
"fullscreen": "View / Exit full screen",
|
"fullscreen": "View / Exit full screen",
|
||||||
|
@ -102,6 +104,7 @@
|
||||||
"cameraDisabled": "Camera is not available",
|
"cameraDisabled": "Camera is not available",
|
||||||
"micDisabled": "Microphone is not available",
|
"micDisabled": "Microphone is not available",
|
||||||
"filmstrip": "Show / Hide videos",
|
"filmstrip": "Show / Hide videos",
|
||||||
|
"pip": "Enter Picture-in-Picture mode",
|
||||||
"profile": "Edit your profile",
|
"profile": "Edit your profile",
|
||||||
"raiseHand": "Raise / Lower your hand",
|
"raiseHand": "Raise / Lower your hand",
|
||||||
"shortcuts": "View shortcuts",
|
"shortcuts": "View shortcuts",
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
export { default as AddPeopleDialog } from './AddPeopleDialog';
|
export { default as AddPeopleDialog } from './AddPeopleDialog';
|
||||||
export { default as InfoDialogButton } from './InfoDialogButton';
|
export { default as InfoDialogButton } from './InfoDialogButton';
|
||||||
export { default as InviteButton } from './InviteButton';
|
|
||||||
export { DialInSummary } from './dial-in-summary';
|
export { DialInSummary } from './dial-in-summary';
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { getAppProp } from '../../../app';
|
|
||||||
import { ToolbarButton } from '../../../toolbox';
|
|
||||||
|
|
||||||
import { enterPictureInPicture } from '../actions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of {@link EnterPictureInPictureToobarButton}'s React
|
|
||||||
* {@code Component} props.
|
|
||||||
*/
|
|
||||||
type Props = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enters (or rather initiates entering) picture-in-picture.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
_onEnterPictureInPicture: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The indicator which determines whether Picture-in-Picture is enabled.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
_pictureInPictureEnabled: boolean
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements a {@link ToolbarButton} to enter Picture-in-Picture.
|
|
||||||
*/
|
|
||||||
class EnterPictureInPictureToolbarButton extends Component<Props> {
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
_onEnterPictureInPicture,
|
|
||||||
_pictureInPictureEnabled,
|
|
||||||
...props
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!_pictureInPictureEnabled) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToolbarButton
|
|
||||||
iconName = { 'menu-down' }
|
|
||||||
onClick = { _onEnterPictureInPicture }
|
|
||||||
{ ...props } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps redux actions to {@link EnterPictureInPictureToolbarButton}'s React
|
|
||||||
* {@code Component} props.
|
|
||||||
*
|
|
||||||
* @param {Function} dispatch - The redux action {@code dispatch} function.
|
|
||||||
* @returns {{
|
|
||||||
* }}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function _mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests Picture-in-Picture mode.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
_onEnterPictureInPicture() {
|
|
||||||
dispatch(enterPictureInPicture());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps (parts of) the redux state to
|
|
||||||
* {@link EnterPictureInPictureToolbarButton}'s React {@code Component} props.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The redux store/state.
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state) {
|
|
||||||
return {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The indicator which determines whether Picture-in-Picture is enabled.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
_pictureInPictureEnabled:
|
|
||||||
Boolean(getAppProp(state, 'pictureInPictureEnabled'))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(_mapStateToProps, _mapDispatchToProps)(
|
|
||||||
EnterPictureInPictureToolbarButton);
|
|
|
@ -1,2 +0,0 @@
|
||||||
export { default as EnterPictureInPictureToolbarButton }
|
|
||||||
from './EnterPictureInPictureToolbarButton';
|
|
|
@ -1,3 +1,2 @@
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
export * from './actionTypes';
|
export * from './actionTypes';
|
||||||
export * from './components';
|
|
||||||
|
|
|
@ -4,91 +4,75 @@ import React, { Component } from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { toggleAudioOnly } from '../../base/conference';
|
|
||||||
import {
|
|
||||||
MEDIA_TYPE,
|
|
||||||
toggleCameraFacingMode
|
|
||||||
} from '../../base/media';
|
|
||||||
import { Container } from '../../base/react';
|
import { Container } from '../../base/react';
|
||||||
import {
|
import {
|
||||||
isNarrowAspectRatio,
|
isNarrowAspectRatio,
|
||||||
makeAspectRatioAware
|
makeAspectRatioAware
|
||||||
} from '../../base/responsive-ui';
|
} from '../../base/responsive-ui';
|
||||||
import { ColorPalette } from '../../base/styles';
|
import { ColorPalette } from '../../base/styles';
|
||||||
import { InviteButton } from '../../invite';
|
|
||||||
import {
|
|
||||||
EnterPictureInPictureToolbarButton
|
|
||||||
} from '../../mobile/picture-in-picture';
|
|
||||||
import { beginRoomLockRequest } from '../../room-lock';
|
|
||||||
|
|
||||||
import {
|
|
||||||
abstractMapDispatchToProps,
|
|
||||||
abstractMapStateToProps
|
|
||||||
} from '../functions';
|
|
||||||
|
|
||||||
import AudioRouteButton from './AudioRouteButton';
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import ToolbarButton from './ToolbarButton';
|
|
||||||
|
|
||||||
import { AudioMuteButton, HangupButton, VideoMuteButton } from './buttons';
|
import {
|
||||||
|
AudioMuteButton,
|
||||||
|
AudioOnlyButton,
|
||||||
|
AudioRouteButton,
|
||||||
|
HangupButton,
|
||||||
|
PictureInPictureButton,
|
||||||
|
RoomLockButton,
|
||||||
|
InviteButton,
|
||||||
|
ToggleCameraButton,
|
||||||
|
VideoMuteButton
|
||||||
|
} from './buttons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Styles for the hangup button.
|
||||||
|
*/
|
||||||
|
const hangupButtonStyles = {
|
||||||
|
iconStyle: styles.whitePrimaryToolbarButtonIcon,
|
||||||
|
style: styles.hangup,
|
||||||
|
underlayColor: ColorPalette.buttonUnderlay
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Styles for buttons in the primary toolbar.
|
||||||
|
*/
|
||||||
|
const primaryToolbarButtonStyles = {
|
||||||
|
iconStyle: styles.primaryToolbarButtonIcon,
|
||||||
|
style: styles.primaryToolbarButton
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Styles for buttons in the primary toolbar.
|
||||||
|
*/
|
||||||
|
const primaryToolbarToggledButtonStyles = {
|
||||||
|
iconStyle: styles.whitePrimaryToolbarButtonIcon,
|
||||||
|
style: styles.whitePrimaryToolbarButton
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Styles for buttons in the secondary toolbar.
|
||||||
|
*/
|
||||||
|
const secondaryToolbarButtonStyles = {
|
||||||
|
iconStyle: styles.secondaryToolbarButtonIcon,
|
||||||
|
style: styles.secondaryToolbarButton,
|
||||||
|
underlayColor: 'transparent'
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of {@link Toolbox}'s React {@code Component} props.
|
* The type of {@link Toolbox}'s React {@code Component} props.
|
||||||
*/
|
*/
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag showing that audio is muted.
|
|
||||||
*/
|
|
||||||
_audioMuted: boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag showing whether the audio-only mode is in use.
|
|
||||||
*/
|
|
||||||
_audioOnly: boolean,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The indicator which determines whether the toolbox is enabled.
|
* The indicator which determines whether the toolbox is enabled.
|
||||||
*/
|
*/
|
||||||
_enabled: boolean,
|
_enabled: boolean,
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag showing whether room is locked.
|
|
||||||
*/
|
|
||||||
_locked: boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for hangup.
|
|
||||||
*/
|
|
||||||
_onHangup: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the lock i.e. password protection of the conference/room.
|
|
||||||
*/
|
|
||||||
_onRoomLock: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the audio-only flag of the conference.
|
|
||||||
*/
|
|
||||||
_onToggleAudioOnly: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Switches between the front/user-facing and back/environment-facing
|
|
||||||
* cameras.
|
|
||||||
*/
|
|
||||||
_onToggleCameraFacingMode: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag showing whether video is muted.
|
|
||||||
*/
|
|
||||||
_videoMuted: boolean,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag showing whether toolbar is visible.
|
* Flag showing whether toolbar is visible.
|
||||||
*/
|
*/
|
||||||
_visible: boolean,
|
_visible: boolean
|
||||||
|
|
||||||
dispatch: Function
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,36 +104,6 @@ class Toolbox extends Component<Props> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the styles for a button that toggles the mute state of a specific
|
|
||||||
* media type.
|
|
||||||
*
|
|
||||||
* @param {string} mediaType - The {@link MEDIA_TYPE} associated with the
|
|
||||||
* button to get styles for.
|
|
||||||
* @protected
|
|
||||||
* @returns {{
|
|
||||||
* iconStyle: Object,
|
|
||||||
* style: Object
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
_getMuteButtonStyles(mediaType) {
|
|
||||||
let iconStyle;
|
|
||||||
let style;
|
|
||||||
|
|
||||||
if (this.props[`_${mediaType}Muted`]) {
|
|
||||||
iconStyle = styles.whitePrimaryToolbarButtonIcon;
|
|
||||||
style = styles.whitePrimaryToolbarButton;
|
|
||||||
} else {
|
|
||||||
iconStyle = styles.primaryToolbarButtonIcon;
|
|
||||||
style = styles.primaryToolbarButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
iconStyle,
|
|
||||||
style
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the toolbar which contains the primary buttons such as hangup,
|
* Renders the toolbar which contains the primary buttons such as hangup,
|
||||||
* audio and video mute.
|
* audio and video mute.
|
||||||
|
@ -158,28 +112,20 @@ class Toolbox extends Component<Props> {
|
||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
_renderPrimaryToolbar() {
|
_renderPrimaryToolbar() {
|
||||||
const audioButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.AUDIO);
|
|
||||||
const videoButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.VIDEO);
|
|
||||||
const hangupButtonStyles = {
|
|
||||||
iconStyle: styles.whitePrimaryToolbarButtonIcon,
|
|
||||||
style: styles.hangup,
|
|
||||||
underlayColor: ColorPalette.buttonUnderlay
|
|
||||||
};
|
|
||||||
|
|
||||||
/* eslint-disable react/jsx-handler-names */
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
key = 'primaryToolbar'
|
key = 'primaryToolbar'
|
||||||
pointerEvents = 'box-none'
|
pointerEvents = 'box-none'
|
||||||
style = { styles.primaryToolbar }>
|
style = { styles.primaryToolbar }>
|
||||||
<AudioMuteButton styles = { audioButtonStyles } />
|
<AudioMuteButton
|
||||||
|
styles = { primaryToolbarButtonStyles }
|
||||||
|
toggledStyles = { primaryToolbarToggledButtonStyles } />
|
||||||
<HangupButton styles = { hangupButtonStyles } />
|
<HangupButton styles = { hangupButtonStyles } />
|
||||||
<VideoMuteButton styles = { videoButtonStyles } />
|
<VideoMuteButton
|
||||||
|
styles = { primaryToolbarButtonStyles }
|
||||||
|
toggledStyles = { primaryToolbarToggledButtonStyles } />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
/* eslint-enable react/jsx-handler-names */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -190,62 +136,20 @@ class Toolbox extends Component<Props> {
|
||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
_renderSecondaryToolbar() {
|
_renderSecondaryToolbar() {
|
||||||
const iconStyle = styles.secondaryToolbarButtonIcon;
|
|
||||||
const style = styles.secondaryToolbarButton;
|
|
||||||
const underlayColor = 'transparent';
|
|
||||||
const {
|
|
||||||
_audioOnly: audioOnly,
|
|
||||||
_videoMuted: videoMuted
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
/* eslint-disable react/jsx-curly-spacing,react/jsx-handler-names */
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
key = 'secondaryToolbar'
|
key = 'secondaryToolbar'
|
||||||
pointerEvents = 'box-none'
|
pointerEvents = 'box-none'
|
||||||
style = { styles.secondaryToolbar }>
|
style = { styles.secondaryToolbar }>
|
||||||
{
|
<AudioRouteButton styles = { secondaryToolbarButtonStyles } />
|
||||||
AudioRouteButton
|
<ToggleCameraButton styles = { secondaryToolbarButtonStyles } />
|
||||||
&& <AudioRouteButton
|
<AudioOnlyButton styles = { secondaryToolbarButtonStyles } />
|
||||||
iconName = { 'volume' }
|
<RoomLockButton styles = { secondaryToolbarButtonStyles } />
|
||||||
iconStyle = { iconStyle }
|
<InviteButton styles = { secondaryToolbarButtonStyles } />
|
||||||
style = { style }
|
<PictureInPictureButton
|
||||||
underlayColor = { underlayColor } />
|
styles = { secondaryToolbarButtonStyles } />
|
||||||
}
|
|
||||||
<ToolbarButton
|
|
||||||
disabled = { audioOnly || videoMuted }
|
|
||||||
iconName = 'switch-camera'
|
|
||||||
iconStyle = { iconStyle }
|
|
||||||
onClick = { this.props._onToggleCameraFacingMode }
|
|
||||||
style = { style }
|
|
||||||
underlayColor = { underlayColor } />
|
|
||||||
<ToolbarButton
|
|
||||||
iconName = { audioOnly ? 'visibility-off' : 'visibility' }
|
|
||||||
iconStyle = { iconStyle }
|
|
||||||
onClick = { this.props._onToggleAudioOnly }
|
|
||||||
style = { style }
|
|
||||||
underlayColor = { underlayColor } />
|
|
||||||
<ToolbarButton
|
|
||||||
iconName = {
|
|
||||||
this.props._locked ? 'security-locked' : 'security'
|
|
||||||
}
|
|
||||||
iconStyle = { iconStyle }
|
|
||||||
onClick = { this.props._onRoomLock }
|
|
||||||
style = { style }
|
|
||||||
underlayColor = { underlayColor } />
|
|
||||||
<InviteButton
|
|
||||||
iconStyle = { iconStyle }
|
|
||||||
style = { style }
|
|
||||||
underlayColor = { underlayColor } />
|
|
||||||
<EnterPictureInPictureToolbarButton
|
|
||||||
iconStyle = { iconStyle }
|
|
||||||
style = { style }
|
|
||||||
underlayColor = { underlayColor } />
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
/* eslint-enable react/jsx-curly-spacing,react/jsx-handler-names */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -263,84 +167,21 @@ class Toolbox extends Component<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps redux actions to {@link Toolbox}'s React {@code Component} props.
|
* Maps parts of the redux state to {@link Toolbox} (React {@code Component})
|
||||||
*
|
|
||||||
* @param {Function} dispatch - The redux action {@code dispatch} function.
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* _onRoomLock: Function,
|
|
||||||
* _onToggleAudioOnly: Function,
|
|
||||||
* _onToggleCameraFacingMode: Function,
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
...abstractMapDispatchToProps(dispatch),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the lock i.e. password protection of the conference/room.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
_onRoomLock() {
|
|
||||||
dispatch(beginRoomLockRequest());
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the audio-only flag of the conference.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
_onToggleAudioOnly() {
|
|
||||||
dispatch(toggleAudioOnly());
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Switches between the front/user-facing and back/environment-facing
|
|
||||||
* cameras.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
_onToggleCameraFacingMode() {
|
|
||||||
dispatch(toggleCameraFacingMode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps (parts of) the redux state to {@link Toolbox}'s React {@code Component}
|
|
||||||
* props.
|
* props.
|
||||||
*
|
*
|
||||||
* @param {Object} state - The redux store/state.
|
* @param {Object} state - The redux state of which parts are to be mapped to
|
||||||
* @private
|
* {@code Toolbox} props.
|
||||||
|
* @protected
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* _audioOnly: boolean,
|
|
||||||
* _enabled: boolean,
|
* _enabled: boolean,
|
||||||
* _locked: boolean
|
* _visible: boolean
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state: Object): Object {
|
||||||
const conference = state['features/base/conference'];
|
const { enabled, visible } = state['features/toolbox'];
|
||||||
const { enabled } = state['features/toolbox'];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...abstractMapStateToProps(state),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The indicator which determines whether the conference is in
|
|
||||||
* audio-only mode.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
_audioOnly: Boolean(conference.audioOnly),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The indicator which determines whether the toolbox is enabled.
|
* The indicator which determines whether the toolbox is enabled.
|
||||||
*
|
*
|
||||||
|
@ -350,15 +191,13 @@ function _mapStateToProps(state) {
|
||||||
_enabled: enabled,
|
_enabled: enabled,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The indicator which determines whether the conference is
|
* Flag showing whether toolbox is visible.
|
||||||
* locked/password-protected.
|
|
||||||
*
|
*
|
||||||
* @protected
|
* @protected
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
_locked: Boolean(conference.locked)
|
_visible: visible
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(_mapStateToProps, _mapDispatchToProps)(
|
export default connect(_mapStateToProps)(makeAspectRatioAware(Toolbox));
|
||||||
makeAspectRatioAware(Toolbox));
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ export type Props = {
|
||||||
/**
|
/**
|
||||||
* An abstract implementation of a button.
|
* An abstract implementation of a button.
|
||||||
*/
|
*/
|
||||||
export default class AbstractButton<P: Props, S : *> extends Component<P, S> {
|
export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
showLabel: false,
|
showLabel: false,
|
||||||
styles: undefined,
|
styles: undefined,
|
||||||
|
@ -173,9 +173,9 @@ export default class AbstractButton<P: Props, S : *> extends Component<P, S> {
|
||||||
* Implements React's {@link Component#render()}.
|
* Implements React's {@link Component#render()}.
|
||||||
*
|
*
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
* @returns {ReactElement}
|
* @returns {React$Node}
|
||||||
*/
|
*/
|
||||||
render() {
|
render(): React$Node {
|
||||||
const props = {
|
const props = {
|
||||||
...this.props,
|
...this.props,
|
||||||
accessibilityLabel: this.accessibilityLabel,
|
accessibilityLabel: this.accessibilityLabel,
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { toggleAudioOnly } from '../../../../base/conference';
|
||||||
|
import { translate } from '../../../../base/i18n';
|
||||||
|
|
||||||
|
import AbstractButton from '../AbstractButton';
|
||||||
|
import type { Props as AbstractButtonProps } from '../AbstractButton';
|
||||||
|
|
||||||
|
type Props = AbstractButtonProps & {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current conference is in audio only mode or not.
|
||||||
|
*/
|
||||||
|
_audioOnly: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The redux {@code dispatch} function.
|
||||||
|
*/
|
||||||
|
dispatch: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of a button for toggling the audio-only mode.
|
||||||
|
*/
|
||||||
|
class AudioOnlyButton extends AbstractButton<Props, *> {
|
||||||
|
accessibilityLabel = 'Audio only mode';
|
||||||
|
iconName = 'visibility';
|
||||||
|
label = 'toolbar.audioonly';
|
||||||
|
toggledIconName = 'visibility-off';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles clicking / pressing the button.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClick() {
|
||||||
|
this.props.dispatch(toggleAudioOnly());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is in toggled state or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isToggled() {
|
||||||
|
return this.props._audioOnly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps (parts of) the redux state to the associated props for the
|
||||||
|
* {@code AudioOnlyButton} component.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The Redux state.
|
||||||
|
* @private
|
||||||
|
* @returns {{
|
||||||
|
* _audioOnly: boolean
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
function _mapStateToProps(state): Object {
|
||||||
|
const { audioOnly } = state['features/base/conference'];
|
||||||
|
|
||||||
|
return {
|
||||||
|
_audioOnly: Boolean(audioOnly)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(connect(_mapStateToProps)(AudioOnlyButton));
|
|
@ -1,6 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
findNodeHandle,
|
findNodeHandle,
|
||||||
NativeModules,
|
NativeModules,
|
||||||
|
@ -9,10 +9,13 @@ import {
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { openDialog } from '../../base/dialog';
|
import { openDialog } from '../../../../base/dialog';
|
||||||
import { AudioRoutePickerDialog } from '../../mobile/audio-mode';
|
import { translate } from '../../../../base/i18n';
|
||||||
|
import { AudioRoutePickerDialog } from '../../../../mobile/audio-mode';
|
||||||
|
|
||||||
|
import AbstractButton from '../AbstractButton';
|
||||||
|
import type { Props as AbstractButtonProps } from '../AbstractButton';
|
||||||
|
|
||||||
import ToolbarButton from './ToolbarButton';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code MPVolumeView} React {@code Component}. It will only be available
|
* The {@code MPVolumeView} React {@code Component}. It will only be available
|
||||||
|
@ -28,48 +31,32 @@ const MPVolumeView
|
||||||
*/
|
*/
|
||||||
const HIDE_VIEW_STYLE = { display: 'none' };
|
const HIDE_VIEW_STYLE = { display: 'none' };
|
||||||
|
|
||||||
type Props = {
|
type Props = AbstractButtonProps & {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The redux {@code dispatch} function used to open/show the
|
* The redux {@code dispatch} function used to open/show the
|
||||||
* {@code AudioRoutePickerDialog}.
|
* {@code AudioRoutePickerDialog}.
|
||||||
*/
|
*/
|
||||||
dispatch: Function,
|
dispatch: Function
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the Icon of this {@code AudioRouteButton}.
|
|
||||||
*/
|
|
||||||
iconName: string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The style of the Icon of this {@code AudioRouteButton}.
|
|
||||||
*/
|
|
||||||
iconStyle: Object,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The style(s) of {@code AudioRouteButton}.
|
|
||||||
*/
|
|
||||||
style: Array<*> | Object,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The color underlaying the button.
|
|
||||||
*/
|
|
||||||
underlayColor: string
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A toolbar button which triggers an audio route picker when pressed.
|
* A toolbar button which triggers an audio route picker when pressed.
|
||||||
*/
|
*/
|
||||||
class AudioRouteButton extends Component<Props> {
|
class AudioRouteButton extends AbstractButton<Props, *> {
|
||||||
|
accessibilityLabel = 'Audio route';
|
||||||
|
iconName = 'icon-volume';
|
||||||
|
label = 'toolbar.audioRoute';
|
||||||
|
|
||||||
_volumeComponent: ?Object;
|
_volumeComponent: ?Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new {@code AudioRouteButton} instance.
|
* Initializes a new {@code AudioRouteButton} instance.
|
||||||
*
|
*
|
||||||
* @param {Object} props - The React {@code Component} props to initialize
|
* @param {Props} props - The React {@code Component} props to initialize
|
||||||
* the new {@code AudioRouteButton} instance with.
|
* the new {@code AudioRouteButton} instance with.
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,25 +64,21 @@ class AudioRouteButton extends Component<Props> {
|
||||||
* showing the volume control view.
|
* showing the volume control view.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @type {ReactComponent}
|
* @type {ReactElement}
|
||||||
*/
|
*/
|
||||||
this._volumeComponent = null;
|
this._volumeComponent = null;
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once per instance.
|
// Bind event handlers so they are only bound once per instance.
|
||||||
this._onClick = this._onClick.bind(this);
|
|
||||||
this._setVolumeComponent = this._setVolumeComponent.bind(this);
|
this._setVolumeComponent = this._setVolumeComponent.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onClick: () => void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles clicking/pressing this {@code AudioRouteButton} by showing an
|
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||||
* audio route picker.
|
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onClick() {
|
_handleClick() {
|
||||||
if (MPVolumeView) {
|
if (MPVolumeView) {
|
||||||
NativeModules.MPVolumeViewManager.show(
|
NativeModules.MPVolumeViewManager.show(
|
||||||
findNodeHandle(this._volumeComponent));
|
findNodeHandle(this._volumeComponent));
|
||||||
|
@ -104,23 +87,49 @@ class AudioRouteButton extends Component<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setVolumeComponent: (?Object) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the internal reference to the React Component wrapping the
|
||||||
|
* {@code MPVolumeView} component.
|
||||||
|
*
|
||||||
|
* @param {ReactElement} component - React Component.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_setVolumeComponent(component) {
|
||||||
|
this._volumeComponent = component;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements React's {@link Component#render()}.
|
* Implements React's {@link Component#render()}.
|
||||||
*
|
*
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
* @returns {ReactElement}
|
* @returns {?ReactElement}
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { iconName, iconStyle, style, underlayColor } = this.props;
|
if (!MPVolumeView && !AudioRoutePickerDialog) {
|
||||||
|
|
||||||
|
// $FlowFixMe
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const element = super.render();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ToolbarButton
|
{ element }
|
||||||
iconName = { iconName }
|
|
||||||
iconStyle = { iconStyle }
|
|
||||||
onClick = { this._onClick }
|
|
||||||
style = { style }
|
|
||||||
underlayColor = { underlayColor } />
|
|
||||||
{
|
{
|
||||||
MPVolumeView
|
MPVolumeView
|
||||||
&& <MPVolumeView
|
&& <MPVolumeView
|
||||||
|
@ -130,21 +139,6 @@ class AudioRouteButton extends Component<Props> {
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_setVolumeComponent: (?Object) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the internal reference to the React Component wrapping the
|
|
||||||
* {@code MPVolumeView} component.
|
|
||||||
*
|
|
||||||
* @param {ReactComponent} component - React Component.
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_setVolumeComponent(component) {
|
|
||||||
this._volumeComponent = component;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (MPVolumeView || AudioRoutePickerDialog)
|
export default translate(connect()(AudioRouteButton));
|
||||||
&& connect()(AudioRouteButton);
|
|
|
@ -1,29 +1,18 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { beginShareRoom } from '../../share-room';
|
import {
|
||||||
import { ToolbarButton } from '../../toolbox';
|
beginAddPeople,
|
||||||
|
isAddPeopleEnabled,
|
||||||
|
isDialOutEnabled
|
||||||
|
} from '../../../../invite';
|
||||||
|
import { beginShareRoom } from '../../../../share-room';
|
||||||
|
|
||||||
import { beginAddPeople } from '../actions';
|
import AbstractButton from '../AbstractButton';
|
||||||
import { isAddPeopleEnabled, isDialOutEnabled } from '../functions';
|
import type { Props as AbstractButtonProps } from '../AbstractButton';
|
||||||
|
|
||||||
/**
|
type Props = AbstractButtonProps & {
|
||||||
* The indicator which determines (at bundle time) whether there should be a
|
|
||||||
* {@code ToolbarButton} in {@code Toolbox} to expose the functionality of the
|
|
||||||
* feature share-room in the user interface of the app.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
const _SHARE_ROOM_TOOLBAR_BUTTON = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of {@link EnterPictureInPictureToobarButton}'s React
|
|
||||||
* {@code Component} props.
|
|
||||||
*/
|
|
||||||
type Props = {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the feature to directly invite people into the
|
* Whether or not the feature to directly invite people into the
|
||||||
|
@ -50,45 +39,71 @@ type Props = {
|
||||||
_onShareRoom: Function
|
_onShareRoom: Function
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The indicator which determines (at bundle time) whether there should be a
|
||||||
|
* button in {@code Toolbox} to expose the functionality of the feature
|
||||||
|
* share-room in the user interface of the app.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
const _SHARE_ROOM_TOOLBAR_BUTTON = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a {@link ToolbarButton} to enter Picture-in-Picture.
|
* Implements a {@link ToolbarButton} to enter Picture-in-Picture.
|
||||||
*/
|
*/
|
||||||
class InviteButton extends Component<Props> {
|
class InviteButton extends AbstractButton<Props, *> {
|
||||||
|
accessibilityLabel = 'Share room';
|
||||||
|
iconName = 'icon-link';
|
||||||
|
label = 'toolbar.shareRoom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClick() {
|
||||||
|
const {
|
||||||
|
_addPeopleEnabled,
|
||||||
|
_dialOutEnabled,
|
||||||
|
_onAddPeople,
|
||||||
|
_onShareRoom
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (_addPeopleEnabled || _dialOutEnabled) {
|
||||||
|
_onAddPeople();
|
||||||
|
} else if (_SHARE_ROOM_TOOLBAR_BUTTON) {
|
||||||
|
_onShareRoom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements React's {@link Component#render()}.
|
* Implements React's {@link Component#render()}.
|
||||||
*
|
*
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
* @returns {ReactElement}
|
* @returns {React$Node}
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { _addPeopleEnabled, _dialOutEnabled } = this.props;
|
||||||
_addPeopleEnabled,
|
|
||||||
_dialOutEnabled,
|
|
||||||
_onAddPeople,
|
|
||||||
_onShareRoom,
|
|
||||||
...props
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (_addPeopleEnabled || _dialOutEnabled) {
|
return (
|
||||||
return (
|
_SHARE_ROOM_TOOLBAR_BUTTON
|
||||||
<ToolbarButton
|
|| _addPeopleEnabled
|
||||||
iconName = { 'link' }
|
|| _dialOutEnabled
|
||||||
onClick = { _onAddPeople }
|
? super.render()
|
||||||
{ ...props } />
|
: null);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_SHARE_ROOM_TOOLBAR_BUTTON) {
|
|
||||||
return (
|
|
||||||
<ToolbarButton
|
|
||||||
iconName = 'link'
|
|
||||||
onClick = { _onShareRoom }
|
|
||||||
{ ...props } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { getAppProp } from '../../../../app';
|
||||||
|
import { translate } from '../../../../base/i18n';
|
||||||
|
import { enterPictureInPicture } from '../../../../mobile/picture-in-picture';
|
||||||
|
|
||||||
|
import AbstractButton from '../AbstractButton';
|
||||||
|
import type { Props as AbstractButtonProps } from '../AbstractButton';
|
||||||
|
|
||||||
|
type Props = AbstractButtonProps & {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether Picture-in-Picture is enabled or not.
|
||||||
|
*/
|
||||||
|
_enabled: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The redux {@code dispatch} function.
|
||||||
|
*/
|
||||||
|
dispatch: Function
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of a button for entering Picture-in-Picture mode.
|
||||||
|
*/
|
||||||
|
class PictureInPictureButton extends AbstractButton<Props, *> {
|
||||||
|
accessibilityLabel = 'Picture in picture';
|
||||||
|
iconName = 'icon-menu-down';
|
||||||
|
label = 'toolbar.pip';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles clicking / pressing the button.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClick() {
|
||||||
|
this.props.dispatch(enterPictureInPicture());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {?ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
if (!this.props._enabled) {
|
||||||
|
|
||||||
|
// $FlowFixMe
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps (parts of) the redux state to the associated props for the
|
||||||
|
* {@code PictureInPictureButton} component.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The Redux state.
|
||||||
|
* @private
|
||||||
|
* @returns {{
|
||||||
|
* _enabled: boolean
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
function _mapStateToProps(state): Object {
|
||||||
|
return {
|
||||||
|
_enabled: Boolean(getAppProp(state, 'pictureInPictureEnabled'))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(connect(_mapStateToProps)(PictureInPictureButton));
|
|
@ -0,0 +1,90 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { translate } from '../../../../base/i18n';
|
||||||
|
import { beginRoomLockRequest } from '../../../../room-lock';
|
||||||
|
|
||||||
|
import AbstractButton from '../AbstractButton';
|
||||||
|
import type { Props as AbstractButtonProps } from '../AbstractButton';
|
||||||
|
|
||||||
|
type Props = AbstractButtonProps & {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current conference.
|
||||||
|
*/
|
||||||
|
_conference: Object,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current conference is locked or not.
|
||||||
|
*/
|
||||||
|
_locked: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The redux {@code dispatch} function.
|
||||||
|
*/
|
||||||
|
dispatch: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of a button for locking / unlocking a room.
|
||||||
|
*/
|
||||||
|
class RoomLockButton extends AbstractButton<Props, *> {
|
||||||
|
accessibilityLabel = 'Room lock';
|
||||||
|
iconName = 'security';
|
||||||
|
label = 'toolbar.lock';
|
||||||
|
toggledIconName = 'security-locked';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles clicking / pressing the button.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClick() {
|
||||||
|
this.props.dispatch(beginRoomLockRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return !this.props._conference;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is in toggled state or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isToggled() {
|
||||||
|
return this.props._locked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps (parts of) the redux state to the associated props for the
|
||||||
|
* {@code RoomLockButton} component.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The Redux state.
|
||||||
|
* @private
|
||||||
|
* @returns {{
|
||||||
|
* _audioOnly: boolean
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
function _mapStateToProps(state): Object {
|
||||||
|
const { conference, locked } = state['features/base/conference'];
|
||||||
|
|
||||||
|
return {
|
||||||
|
_conference: conference,
|
||||||
|
_locked: Boolean(conference && locked)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(connect(_mapStateToProps)(RoomLockButton));
|
|
@ -0,0 +1,50 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { translate } from '../../../../base/i18n';
|
||||||
|
import { beginShareRoom } from '../../../../share-room';
|
||||||
|
|
||||||
|
import AbstractButton from '../AbstractButton';
|
||||||
|
import type { Props as AbstractButtonProps } from '../AbstractButton';
|
||||||
|
|
||||||
|
type Props = AbstractButtonProps & {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The redux {@code dispatch} function.
|
||||||
|
*/
|
||||||
|
dispatch: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of a button for sharing a room using the native OS sharing
|
||||||
|
* capabilities.
|
||||||
|
*/
|
||||||
|
class ShareRoomButton extends AbstractButton<Props, *> {
|
||||||
|
accessibilityLabel = 'Share room';
|
||||||
|
iconName = 'icon-link';
|
||||||
|
label = 'toolbar.shareRoom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClick() {
|
||||||
|
this.props.dispatch(beginShareRoom());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(connect()(ShareRoomButton));
|
|
@ -0,0 +1,81 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { translate } from '../../../../base/i18n';
|
||||||
|
import { MEDIA_TYPE, toggleCameraFacingMode } from '../../../../base/media';
|
||||||
|
import { isLocalTrackMuted } from '../../../../base/tracks';
|
||||||
|
|
||||||
|
import AbstractButton from '../AbstractButton';
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of a button for toggling the camera facing mode.
|
||||||
|
*/
|
||||||
|
class ToggleCameraButton extends AbstractButton<Props, *> {
|
||||||
|
accessibilityLabel = 'Share room';
|
||||||
|
iconName = 'icon-switch-camera';
|
||||||
|
label = 'toolbar.switchCamera';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles clicking / pressing the button.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClick() {
|
||||||
|
this.props.dispatch(toggleCameraFacingMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @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.
|
||||||
|
* @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)(ToggleCameraButton));
|
|
@ -0,0 +1,6 @@
|
||||||
|
export { default as AudioOnlyButton } from './AudioOnlyButton';
|
||||||
|
export { default as AudioRouteButton } from './AudioRouteButton';
|
||||||
|
export { default as InviteButton } from './InviteButton';
|
||||||
|
export { default as PictureInPictureButton } from './PictureInPictureButton';
|
||||||
|
export { default as RoomLockButton } from './RoomLockButton';
|
||||||
|
export { default as ToggleCameraButton } from './ToggleCameraButton';
|
|
@ -80,6 +80,15 @@ export default createStyleSheet({
|
||||||
backgroundColor: ColorPalette.red
|
backgroundColor: ColorPalette.red
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The icon style of toolbar buttons in {@link #primaryToolbar} which
|
||||||
|
* hangs the current conference up.
|
||||||
|
*/
|
||||||
|
hangupButtonIcon: {
|
||||||
|
...primaryToolbarButtonIcon,
|
||||||
|
color: ColorPalette.white
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The style of the toolbar which contains the primary buttons such as
|
* The style of the toolbar which contains the primary buttons such as
|
||||||
* hangup, audio and video mute.
|
* hangup, audio and video mute.
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import { appNavigate } from '../app';
|
|
||||||
import { MEDIA_TYPE } from '../base/media';
|
|
||||||
import { isLocalTrackMuted } from '../base/tracks';
|
|
||||||
|
|
||||||
import type { Dispatch } from 'redux';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps redux actions to {@link Toolbox} (React {@code Component}) props.
|
|
||||||
*
|
|
||||||
* @param {Function} dispatch - The redux {@code dispatch} function.
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* _onHangup: Function,
|
|
||||||
* _onToggleAudio: Function,
|
|
||||||
* _onToggleVideo: Function
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
export function abstractMapDispatchToProps(dispatch: Dispatch<*>): Object {
|
|
||||||
return {
|
|
||||||
// Inject {@code dispatch} into the React Component's props in case it
|
|
||||||
// needs to dispatch an action in the redux store without
|
|
||||||
// {@code mapDispatchToProps}.
|
|
||||||
dispatch,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches action to leave the current conference.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
_onHangup() {
|
|
||||||
// XXX We don't know here which value is effectively/internally
|
|
||||||
// used when there's no valid room name to join. It isn't our
|
|
||||||
// business to know that anyway. The undefined value is our
|
|
||||||
// expression of (1) the lack of knowledge & (2) the desire to no
|
|
||||||
// longer have a valid room name to join.
|
|
||||||
dispatch(appNavigate(undefined));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps parts of the redux state to {@link Toolbox} (React {@code Component})
|
|
||||||
* props.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The redux state of which parts are to be mapped to
|
|
||||||
* {@code Toolbox} props.
|
|
||||||
* @protected
|
|
||||||
* @returns {{
|
|
||||||
* _audioMuted: boolean,
|
|
||||||
* _videoMuted: boolean,
|
|
||||||
* _visible: boolean
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
export function abstractMapStateToProps(state: Object): Object {
|
|
||||||
const tracks = state['features/base/tracks'];
|
|
||||||
const { visible } = state['features/toolbox'];
|
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Flag showing whether audio is muted.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
_audioMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag showing whether video is muted.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
_videoMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag showing whether toolbox is visible.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
_visible: visible
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the button object corresponding to a specific {@code buttonName}.
|
|
||||||
*
|
|
||||||
* @param {string} buttonName - The name of the button.
|
|
||||||
* @param {Object} state - The current state.
|
|
||||||
* @returns {Object} - The button object.
|
|
||||||
*/
|
|
||||||
export function getButton(buttonName: string, state: Object) {
|
|
||||||
const { primaryToolbarButtons, secondaryToolbarButtons }
|
|
||||||
= state['features/toolbox'];
|
|
||||||
|
|
||||||
return primaryToolbarButtons.get(buttonName)
|
|
||||||
|| secondaryToolbarButtons.get(buttonName);
|
|
||||||
}
|
|
|
@ -2,12 +2,6 @@
|
||||||
|
|
||||||
declare var interfaceConfig: Object;
|
declare var interfaceConfig: Object;
|
||||||
|
|
||||||
export {
|
|
||||||
abstractMapDispatchToProps,
|
|
||||||
abstractMapStateToProps,
|
|
||||||
getButton
|
|
||||||
} from './functions.native';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for getting the height of the toolbox.
|
* Helper for getting the height of the toolbox.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue