// @flow import _ from 'lodash'; import React, { Component } from 'react'; import { connect as reactReduxConnect } from 'react-redux'; import VideoLayout from '../../../../modules/UI/videolayout/VideoLayout'; import { obtainConfig } from '../../base/config'; import { connect, disconnect } from '../../base/connection'; import { DialogContainer } from '../../base/dialog'; import { translate } from '../../base/i18n'; import { Filmstrip } from '../../filmstrip'; import { CalleeInfoContainer } from '../../invite'; import { LargeVideo } from '../../large-video'; import { NotificationsContainer } from '../../notifications'; import { SidePanel } from '../../side-panel'; import { LAYOUTS, getCurrentLayout, shouldDisplayTileView } from '../../video-layout'; import { default as Notice } from './Notice'; import { Toolbox, fullScreenChanged, setToolboxAlwaysVisible, showToolbox } from '../../toolbox'; import { maybeShowSuboptimalExperienceNotification } from '../functions'; declare var APP: Object; declare var config: Object; declare var interfaceConfig: Object; const logger = require('jitsi-meet-logger').getLogger(__filename); /** * DOM events for when full screen mode has changed. Different browsers need * different vendor prefixes. * * @private * @type {Array} */ const FULL_SCREEN_EVENTS = [ 'webkitfullscreenchange', 'mozfullscreenchange', 'fullscreenchange' ]; /** * The CSS class to apply to the root element of the conference so CSS can * modify the app layout. * * @private * @type {Object} */ const LAYOUT_CLASSNAMES = { [LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'horizontal-filmstrip', [LAYOUTS.TILE_VIEW]: 'tile-view', [LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'vertical-filmstrip' }; /** * The type of the React {@code Component} props of {@link Conference}. */ type Props = { /** * Whether the local participant is recording the conference. */ _iAmRecorder: boolean, /** * The CSS class to apply to the root of {@link Conference} to modify the * application layout. */ _layoutClassName: string, /** * Conference room name. */ _room: string, /** * Whether or not the current UI layout should be in tile view. */ _shouldDisplayTileView: boolean, dispatch: Function, t: Function } /** * The conference page of the Web application. */ class Conference extends Component { _onFullScreenChange: Function; _onShowToolbar: Function; _originalOnShowToolbar: Function; /** * Initializes a new Conference instance. * * @param {Object} props - The read-only properties with which the new * instance is to be initialized. */ constructor(props) { super(props); // Throttle and bind this component's mousemove handler to prevent it // from firing too often. this._originalOnShowToolbar = this._onShowToolbar; this._onShowToolbar = _.throttle( () => this._originalOnShowToolbar(), 100, { leading: true, trailing: false }); // Bind event handler so it is only bound once for every instance. this._onFullScreenChange = this._onFullScreenChange.bind(this); } /** * Start the connection and get the UI ready for the conference. * * @inheritdoc */ componentDidMount() { const { configLocation } = config; if (configLocation) { obtainConfig(configLocation, this.props._room) .then(() => { const now = window.performance.now(); APP.connectionTimes['configuration.fetched'] = now; logger.log('(TIME) configuration fetched:\t', now); this._start(); }) .catch(err => { logger.log(err); // Show obtain config error. APP.UI.messageHandler.showError({ descriptionKey: 'dialog.connectError', titleKey: 'connection.CONNFAIL' }); }); } else { this._start(); } } /** * Calls into legacy UI to update the application layout, if necessary. * * @inheritdoc * returns {void} */ componentDidUpdate(prevProps) { if (this.props._shouldDisplayTileView === prevProps._shouldDisplayTileView) { return; } // TODO: For now VideoLayout is being called as LargeVideo and Filmstrip // sizing logic is still handled outside of React. Once all components // are in react they should calculate size on their own as much as // possible and pass down sizings. VideoLayout.refreshLayout(); } /** * Disconnect from the conference when component will be * unmounted. * * @inheritdoc */ componentWillUnmount() { APP.UI.unregisterListeners(); APP.UI.unbindEvents(); FULL_SCREEN_EVENTS.forEach(name => document.removeEventListener(name, this._onFullScreenChange)); APP.conference.isJoined() && this.props.dispatch(disconnect()); } /** * Implements React's {@link Component#render()}. * * @inheritdoc * @returns {ReactElement} */ render() { const { VIDEO_QUALITY_LABEL_DISABLED, // XXX The character casing of the name filmStripOnly utilized by // interfaceConfig is obsolete but legacy support is required. filmStripOnly: filmstripOnly } = interfaceConfig; const hideVideoQualityLabel = filmstripOnly || VIDEO_QUALITY_LABEL_DISABLED || this.props._iAmRecorder; return (
{ filmstripOnly || } { filmstripOnly || }
); } /** * Updates the Redux state when full screen mode has been enabled or * disabled. * * @private * @returns {void} */ _onFullScreenChange() { this.props.dispatch(fullScreenChanged(APP.UI.isFullScreen())); } /** * Displays the toolbar. * * @private * @returns {void} */ _onShowToolbar() { this.props.dispatch(showToolbox()); } /** * Until we don't rewrite UI using react components * we use UI.start from old app. Also method translates * component right after it has been mounted. * * @inheritdoc */ _start() { APP.UI.start(); APP.UI.registerListeners(); APP.UI.bindEvents(); FULL_SCREEN_EVENTS.forEach(name => document.addEventListener(name, this._onFullScreenChange)); const { dispatch, t } = this.props; dispatch(connect()); maybeShowSuboptimalExperienceNotification(dispatch, t); interfaceConfig.filmStripOnly && dispatch(setToolboxAlwaysVisible(true)); } } /** * Maps (parts of) the Redux state to the associated props for the * {@code Conference} component. * * @param {Object} state - The Redux state. * @private * @returns {{ * _iAmRecorder: boolean, * _layoutClassName: string, * _room: ?string, * _shouldDisplayTileView: boolean * }} */ function _mapStateToProps(state) { const currentLayout = getCurrentLayout(state); return { _iAmRecorder: state['features/base/config'].iAmRecorder, _layoutClassName: LAYOUT_CLASSNAMES[currentLayout], _room: state['features/base/conference'].room, _shouldDisplayTileView: shouldDisplayTileView(state) }; } export default reactReduxConnect(_mapStateToProps)(translate(Conference));