313 lines
8.3 KiB
JavaScript
313 lines
8.3 KiB
JavaScript
// @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 { translate } from '../../../base/i18n';
|
|
import { Chat } from '../../../chat';
|
|
import { Filmstrip } from '../../../filmstrip';
|
|
import { CalleeInfoContainer } from '../../../invite';
|
|
import { LargeVideo } from '../../../large-video';
|
|
import { NotificationsContainer } from '../../../notifications';
|
|
import {
|
|
LAYOUTS,
|
|
getCurrentLayout,
|
|
shouldDisplayTileView
|
|
} from '../../../video-layout';
|
|
|
|
import {
|
|
Toolbox,
|
|
fullScreenChanged,
|
|
setToolboxAlwaysVisible,
|
|
showToolbox
|
|
} from '../../../toolbox';
|
|
|
|
import { maybeShowSuboptimalExperienceNotification } from '../../functions';
|
|
|
|
import Labels from './Labels';
|
|
import { default as Notice } from './Notice';
|
|
import { default as Subject } from './Subject';
|
|
|
|
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<string>}
|
|
*/
|
|
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<Props> {
|
|
_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.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 (
|
|
<div
|
|
className = { this.props._layoutClassName }
|
|
id = 'videoconference_page'
|
|
onMouseMove = { this._onShowToolbar }>
|
|
<Notice />
|
|
<Subject />
|
|
<div id = 'videospace'>
|
|
<LargeVideo />
|
|
{ hideVideoQualityLabel
|
|
|| <Labels /> }
|
|
<Filmstrip filmstripOnly = { filmstripOnly } />
|
|
</div>
|
|
|
|
{ filmstripOnly || <Toolbox /> }
|
|
{ filmstripOnly || <Chat /> }
|
|
|
|
<NotificationsContainer />
|
|
|
|
<CalleeInfoContainer />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
};
|
|
}
|
|
|
|
// $FlowExpectedError
|
|
export default reactReduxConnect(_mapStateToProps)(translate(Conference));
|