jiti-meet/react/features/toolbox/components/Toolbox.native.js

505 lines
14 KiB
JavaScript
Raw Normal View History

// @flow
2017-02-16 23:02:40 +00:00
import React, { Component } from 'react';
import { View } from 'react-native';
import { connect } from 'react-redux';
import {
Restructures the analytics events (#2333) * ref: Restructures the pinned/unpinned events. * ref: Refactors the "audio only disabled" event. * ref: Refactors the "stream switch delay" event. * ref: Refactors the "select participant failed" event. * ref: Refactors the "initially muted" events. * ref: Refactors the screen sharing started/stopped events. * ref: Restructures the "device list changed" events. * ref: Restructures the "shared video" events. * ref: Restructures the "start muted" events. * ref: Restructures the "start audio only" event. * ref: Restructures the "sync track state" event. * ref: Restructures the "callkit" events. * ref: Restructures the "replace track". * ref: Restructures keyboard shortcuts events. * ref: Restructures most of the toolbar events. * ref: Refactors the API events. * ref: Restructures the video quality, profile button and invite dialog events. * ref: Refactors the "device changed" events. * ref: Refactors the page reload event. * ref: Removes an unused function. * ref: Removes a method which is needlessly exposed under a different name. * ref: Refactors the events from the remote video menu. * ref: Refactors the events from the profile pane. * ref: Restructures the recording-related events. Removes events fired when recording with something other than jibri (which isn't currently supported anyway). * ref: Cleans up AnalyticsEvents.js. * ref: Removes an unused function and adds documentation. * feat: Adds events for all API calls. * fix: Addresses feedback. * fix: Brings back mistakenly removed code. * fix: Simplifies code and fixes a bug in toggleFilmstrip when the 'visible' parameter is defined. * feat: Removes the resolution change application log. * ref: Uses consistent naming for events' attributes. Uses "_" as a separator instead of camel case or ".". * ref: Don't add the user agent and conference name as permanent properties. The library does this on its own now. * ref: Adapts the GA handler to changes in lib-jitsi-meet. * ref: Removes unused fields from the analytics handler initializaiton. * ref: Renames the google analytics file and add docs. * fix: Fixes the push-to-talk events and logs. * npm: Updates lib-jitsi-meet to 515374c8d383cb17df8ed76427e6f0fb5ea6ff1e. * fix: Fixes a recently introduced bug in the google analytics handler. * ref: Uses "value" instead of "delay" since this is friendlier to GA.
2018-01-03 21:24:07 +00:00
AUDIO_MUTE,
VIDEO_MUTE,
createToolbarEvent,
sendAnalytics
} from '../../analytics';
import { toggleAudioOnly } from '../../base/conference';
import {
MEDIA_TYPE,
setAudioMuted,
setVideoMuted,
toggleCameraFacingMode,
VIDEO_MUTISM_AUTHORITY
} from '../../base/media';
import { Container } from '../../base/react';
import {
isNarrowAspectRatio,
makeAspectRatioAware
} from '../../base/responsive-ui';
import { ColorPalette } from '../../base/styles';
import {
EnterPictureInPictureToolbarButton
} from '../../mobile/picture-in-picture';
2017-02-16 23:02:40 +00:00
import { beginRoomLockRequest } from '../../room-lock';
import { beginShareRoom } from '../../share-room';
2017-02-16 23:02:40 +00:00
import {
abstractMapDispatchToProps,
abstractMapStateToProps
} from '../functions';
import AudioRouteButton from './AudioRouteButton';
import styles from './styles';
import ToolbarButton from './ToolbarButton';
/**
* 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 Toolbox}'s React {@code Component} 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.
*/
_enabled: boolean,
/**
* Flag showing whether room is locked.
*/
_locked: boolean,
2017-02-16 23:02:40 +00:00
/**
* Handler for hangup.
*/
_onHangup: Function,
/**
* Sets the lock i.e. password protection of the conference/room.
*/
_onRoomLock: Function,
2017-02-16 23:02:40 +00:00
/**
* Begins the UI procedure to share the conference/room URL.
*/
_onShareRoom: Function,
/**
* Toggles the audio-only flag of the conference.
*/
_onToggleAudioOnly: Function,
/**
* Switches between the front/user-facing and back/environment-facing
* cameras.
*/
_onToggleCameraFacingMode: Function,
2017-02-16 23:02:40 +00:00
/**
* Flag showing whether video is muted.
*/
_videoMuted: boolean,
/**
* Flag showing whether toolbar is visible.
*/
_visible: boolean,
2017-02-16 23:02:40 +00:00
dispatch: Function
};
/**
* Implements the conference toolbox on React Native.
*/
class Toolbox extends Component<Props> {
/**
* Initializes a new {@code Toolbox} instance.
*
* @param {Props} props - The read-only React {@code Component} props with
* which the new instance is to be initialized.
*/
constructor(props: Props) {
super(props);
// Bind event handlers so they are only bound once per instance.
this._onToggleAudio = this._onToggleAudio.bind(this);
this._onToggleVideo = this._onToggleVideo.bind(this);
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
if (!this.props._enabled) {
return null;
}
2017-11-07 14:28:08 +00:00
const toolboxStyle
= isNarrowAspectRatio(this)
? styles.toolboxNarrow
: styles.toolboxWide;
return (
<Container
2017-11-07 14:28:08 +00:00
style = { toolboxStyle }
visible = { this.props._visible } >
{ this._renderToolbars() }
</Container>
);
}
2017-02-16 23:02:40 +00:00
/**
* 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 {{
* iconName: string,
* iconStyle: Object,
* style: Object
* }}
*/
_getMuteButtonStyles(mediaType) {
let iconName;
let iconStyle;
let style;
2017-02-16 23:02:40 +00:00
if (this.props[`_${mediaType}Muted`]) {
iconName = `${mediaType}MutedIcon`;
iconStyle = styles.whitePrimaryToolbarButtonIcon;
style = styles.whitePrimaryToolbarButton;
2017-02-16 23:02:40 +00:00
} else {
iconName = `${mediaType}Icon`;
iconStyle = styles.primaryToolbarButtonIcon;
style = styles.primaryToolbarButton;
2017-02-16 23:02:40 +00:00
}
return {
// $FlowExpectedError
iconName: this[iconName],
2017-02-16 23:02:40 +00:00
iconStyle,
style
};
}
_onToggleAudio: () => void;
/**
* Dispatches an action to toggle the mute state of the audio/microphone.
*
* @private
* @returns {void}
*/
_onToggleAudio() {
const mute = !this.props._audioMuted;
Restructures the analytics events (#2333) * ref: Restructures the pinned/unpinned events. * ref: Refactors the "audio only disabled" event. * ref: Refactors the "stream switch delay" event. * ref: Refactors the "select participant failed" event. * ref: Refactors the "initially muted" events. * ref: Refactors the screen sharing started/stopped events. * ref: Restructures the "device list changed" events. * ref: Restructures the "shared video" events. * ref: Restructures the "start muted" events. * ref: Restructures the "start audio only" event. * ref: Restructures the "sync track state" event. * ref: Restructures the "callkit" events. * ref: Restructures the "replace track". * ref: Restructures keyboard shortcuts events. * ref: Restructures most of the toolbar events. * ref: Refactors the API events. * ref: Restructures the video quality, profile button and invite dialog events. * ref: Refactors the "device changed" events. * ref: Refactors the page reload event. * ref: Removes an unused function. * ref: Removes a method which is needlessly exposed under a different name. * ref: Refactors the events from the remote video menu. * ref: Refactors the events from the profile pane. * ref: Restructures the recording-related events. Removes events fired when recording with something other than jibri (which isn't currently supported anyway). * ref: Cleans up AnalyticsEvents.js. * ref: Removes an unused function and adds documentation. * feat: Adds events for all API calls. * fix: Addresses feedback. * fix: Brings back mistakenly removed code. * fix: Simplifies code and fixes a bug in toggleFilmstrip when the 'visible' parameter is defined. * feat: Removes the resolution change application log. * ref: Uses consistent naming for events' attributes. Uses "_" as a separator instead of camel case or ".". * ref: Don't add the user agent and conference name as permanent properties. The library does this on its own now. * ref: Adapts the GA handler to changes in lib-jitsi-meet. * ref: Removes unused fields from the analytics handler initializaiton. * ref: Renames the google analytics file and add docs. * fix: Fixes the push-to-talk events and logs. * npm: Updates lib-jitsi-meet to 515374c8d383cb17df8ed76427e6f0fb5ea6ff1e. * fix: Fixes a recently introduced bug in the google analytics handler. * ref: Uses "value" instead of "delay" since this is friendlier to GA.
2018-01-03 21:24:07 +00:00
sendAnalytics(createToolbarEvent(
AUDIO_MUTE,
{
enable: mute
}));
// The user sees the reality i.e. the state of base/tracks and intends
// 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(
mute,
VIDEO_MUTISM_AUTHORITY.USER,
/* ensureTrack */ true));
}
_onToggleVideo: () => void;
/**
* Dispatches an action to toggle the mute state of the video/camera.
*
* @private
* @returns {void}
*/
_onToggleVideo() {
const mute = !this.props._videoMuted;
Restructures the analytics events (#2333) * ref: Restructures the pinned/unpinned events. * ref: Refactors the "audio only disabled" event. * ref: Refactors the "stream switch delay" event. * ref: Refactors the "select participant failed" event. * ref: Refactors the "initially muted" events. * ref: Refactors the screen sharing started/stopped events. * ref: Restructures the "device list changed" events. * ref: Restructures the "shared video" events. * ref: Restructures the "start muted" events. * ref: Restructures the "start audio only" event. * ref: Restructures the "sync track state" event. * ref: Restructures the "callkit" events. * ref: Restructures the "replace track". * ref: Restructures keyboard shortcuts events. * ref: Restructures most of the toolbar events. * ref: Refactors the API events. * ref: Restructures the video quality, profile button and invite dialog events. * ref: Refactors the "device changed" events. * ref: Refactors the page reload event. * ref: Removes an unused function. * ref: Removes a method which is needlessly exposed under a different name. * ref: Refactors the events from the remote video menu. * ref: Refactors the events from the profile pane. * ref: Restructures the recording-related events. Removes events fired when recording with something other than jibri (which isn't currently supported anyway). * ref: Cleans up AnalyticsEvents.js. * ref: Removes an unused function and adds documentation. * feat: Adds events for all API calls. * fix: Addresses feedback. * fix: Brings back mistakenly removed code. * fix: Simplifies code and fixes a bug in toggleFilmstrip when the 'visible' parameter is defined. * feat: Removes the resolution change application log. * ref: Uses consistent naming for events' attributes. Uses "_" as a separator instead of camel case or ".". * ref: Don't add the user agent and conference name as permanent properties. The library does this on its own now. * ref: Adapts the GA handler to changes in lib-jitsi-meet. * ref: Removes unused fields from the analytics handler initializaiton. * ref: Renames the google analytics file and add docs. * fix: Fixes the push-to-talk events and logs. * npm: Updates lib-jitsi-meet to 515374c8d383cb17df8ed76427e6f0fb5ea6ff1e. * fix: Fixes a recently introduced bug in the google analytics handler. * ref: Uses "value" instead of "delay" since this is friendlier to GA.
2018-01-03 21:24:07 +00:00
sendAnalytics(createToolbarEvent(
VIDEO_MUTE,
{
enable: mute
}));
// The user sees the reality i.e. the state of base/tracks and intends
// 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));
}
/**
* Renders the toolbar which contains the primary buttons such as hangup,
* audio and video mute.
*
* @private
* @returns {ReactElement}
*/
_renderPrimaryToolbar() {
const audioButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.AUDIO);
const videoButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.VIDEO);
/* eslint-disable react/jsx-handler-names */
return (
2017-11-07 23:26:23 +00:00
<View
key = 'primaryToolbar'
style = { styles.primaryToolbar }>
<ToolbarButton
iconName = { audioButtonStyles.iconName }
iconStyle = { audioButtonStyles.iconStyle }
onClick = { this._onToggleAudio }
2016-12-12 19:49:23 +00:00
style = { audioButtonStyles.style } />
<ToolbarButton
accessibilityLabel = 'Hangup'
iconName = 'hangup'
iconStyle = { styles.whitePrimaryToolbarButtonIcon }
2017-02-16 23:02:40 +00:00
onClick = { this.props._onHangup }
style = { styles.hangup }
underlayColor = { ColorPalette.buttonUnderlay } />
<ToolbarButton
2017-07-19 21:25:06 +00:00
disabled = { this.props._audioOnly }
iconName = { videoButtonStyles.iconName }
iconStyle = { videoButtonStyles.iconStyle }
onClick = { this._onToggleVideo }
2016-12-12 19:49:23 +00:00
style = { videoButtonStyles.style } />
</View>
);
/* eslint-enable react/jsx-handler-names */
}
/**
* Renders the toolbar which contains the secondary buttons such as toggle
* camera facing mode.
*
* @private
* @returns {ReactElement}
*/
_renderSecondaryToolbar() {
const iconStyle = styles.secondaryToolbarButtonIcon;
2016-12-12 19:49:23 +00:00
const style = styles.secondaryToolbarButton;
const underlayColor = 'transparent';
const {
_audioOnly: audioOnly,
_videoMuted: videoMuted
} = this.props;
2016-12-12 19:49:23 +00:00
/* eslint-disable react/jsx-curly-spacing,react/jsx-handler-names */
return (
2017-11-07 23:26:23 +00:00
<View
key = 'secondaryToolbar'
style = { styles.secondaryToolbar }>
{
AudioRouteButton
&& <AudioRouteButton
iconName = { 'volume' }
iconStyle = { iconStyle }
style = { style }
underlayColor = { underlayColor } />
}
<ToolbarButton
disabled = { audioOnly || videoMuted }
iconName = 'switch-camera'
2016-12-12 19:49:23 +00:00
iconStyle = { iconStyle }
2017-02-16 23:02:40 +00:00
onClick = { this.props._onToggleCameraFacingMode }
2016-12-12 19:49:23 +00:00
style = { style }
underlayColor = { underlayColor } />
<ToolbarButton
iconName = { audioOnly ? 'visibility-off' : 'visibility' }
2016-12-12 19:49:23 +00:00
iconStyle = { iconStyle }
onClick = { this.props._onToggleAudioOnly }
2016-12-12 19:49:23 +00:00
style = { style }
underlayColor = { underlayColor } />
<ToolbarButton
iconName = {
this.props._locked ? 'security-locked' : 'security'
}
iconStyle = { iconStyle }
onClick = { this.props._onRoomLock }
style = { style }
underlayColor = { underlayColor } />
{
_SHARE_ROOM_TOOLBAR_BUTTON
&& <ToolbarButton
iconName = 'link'
iconStyle = { iconStyle }
onClick = { this.props._onShareRoom }
style = { style }
underlayColor = { underlayColor } />
}
<EnterPictureInPictureToolbarButton
iconStyle = { iconStyle }
style = { style }
underlayColor = { underlayColor } />
</View>
);
2016-12-12 19:49:23 +00:00
/* eslint-enable react/jsx-curly-spacing,react/jsx-handler-names */
}
/**
2017-11-07 14:28:08 +00:00
* Renders the primary and the secondary toolbars.
*
* @private
2017-11-07 14:28:08 +00:00
* @returns {[ReactElement, ReactElement]}
*/
_renderToolbars() {
2017-11-07 14:28:08 +00:00
return [
this._renderSecondaryToolbar(),
this._renderPrimaryToolbar()
];
}
}
/**
* Additional properties for various icons, which are now platform-dependent.
* This is done to have common logic of generating styles for web and native.
* TODO As soon as we have common font sets for web and native, this will no
* longer be required.
*/
// $FlowExpectedError
2017-04-01 05:52:40 +00:00
Object.assign(Toolbox.prototype, {
audioIcon: 'microphone',
audioMutedIcon: 'mic-disabled',
videoIcon: 'camera',
videoMutedIcon: 'camera-disabled'
});
2017-02-16 23:02:40 +00:00
/**
* Maps redux actions to {@link Toolbox}'s React {@code Component} props.
2017-02-16 23:02:40 +00:00
*
* @param {Function} dispatch - The redux action {@code dispatch} function.
* @private
2017-02-16 23:02:40 +00:00
* @returns {{
* _onRoomLock: Function,
* _onToggleAudioOnly: Function,
2017-02-16 23:02:40 +00:00
* _onToggleCameraFacingMode: Function,
* }}
*/
function _mapDispatchToProps(dispatch) {
return {
...abstractMapDispatchToProps(dispatch),
/**
* Sets the lock i.e. password protection of the conference/room.
2017-02-16 23:02:40 +00:00
*
* @private
2017-08-14 10:23:46 +00:00
* @returns {void}
2017-02-16 23:02:40 +00:00
* @type {Function}
*/
_onRoomLock() {
dispatch(beginRoomLockRequest());
2017-02-16 23:02:40 +00:00
},
/**
* Begins the UI procedure to share the conference/room URL.
*
* @private
2017-08-14 10:23:46 +00:00
* @returns {void}
* @type {Function}
*/
_onShareRoom() {
dispatch(beginShareRoom());
},
2017-02-16 23:02:40 +00:00
/**
* Toggles the audio-only flag of the conference.
*
* @private
2017-08-14 10:23:46 +00:00
* @returns {void}
* @type {Function}
*/
_onToggleAudioOnly() {
dispatch(toggleAudioOnly());
},
/**
* Switches between the front/user-facing and back/environment-facing
2017-02-16 23:02:40 +00:00
* cameras.
*
* @private
2017-08-14 10:23:46 +00:00
* @returns {void}
2017-02-16 23:02:40 +00:00
* @type {Function}
*/
_onToggleCameraFacingMode() {
dispatch(toggleCameraFacingMode());
2017-02-16 23:02:40 +00:00
}
};
}
/**
* Maps (parts of) the redux state to {@link Toolbox}'s React {@code Component}
* props.
2017-02-16 23:02:40 +00:00
*
* @param {Object} state - The redux store/state.
* @private
2017-02-16 23:02:40 +00:00
* @returns {{
* _audioOnly: boolean,
* _enabled: boolean,
2017-02-16 23:02:40 +00:00
* _locked: boolean
* }}
*/
function _mapStateToProps(state) {
const conference = state['features/base/conference'];
const { enabled } = state['features/toolbox'];
2017-02-16 23:02:40 +00:00
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.
*
* @private
* @type {boolean}
*/
_enabled: enabled,
2017-02-16 23:02:40 +00:00
/**
* The indicator which determines whether the conference is
* locked/password-protected.
*
* @protected
* @type {boolean}
*/
_locked: Boolean(conference.locked)
2017-02-16 23:02:40 +00:00
};
}
export default connect(_mapStateToProps, _mapDispatchToProps)(
2017-11-03 20:14:38 +00:00
makeAspectRatioAware(Toolbox));