You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Conference.js 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // @flow
  2. import _ from 'lodash';
  3. import React, { Component } from 'react';
  4. import { connect as reactReduxConnect } from 'react-redux';
  5. import VideoLayout from '../../../../../modules/UI/videolayout/VideoLayout';
  6. import { obtainConfig } from '../../../base/config';
  7. import { connect, disconnect } from '../../../base/connection';
  8. import { translate } from '../../../base/i18n';
  9. import { Chat } from '../../../chat';
  10. import { Filmstrip } from '../../../filmstrip';
  11. import { CalleeInfoContainer } from '../../../invite';
  12. import { LargeVideo } from '../../../large-video';
  13. import { NotificationsContainer } from '../../../notifications';
  14. import {
  15. LAYOUTS,
  16. getCurrentLayout,
  17. shouldDisplayTileView
  18. } from '../../../video-layout';
  19. import {
  20. Toolbox,
  21. fullScreenChanged,
  22. setToolboxAlwaysVisible,
  23. showToolbox
  24. } from '../../../toolbox';
  25. import { maybeShowSuboptimalExperienceNotification } from '../../functions';
  26. import Labels from './Labels';
  27. import { default as Notice } from './Notice';
  28. declare var APP: Object;
  29. declare var config: Object;
  30. declare var interfaceConfig: Object;
  31. const logger = require('jitsi-meet-logger').getLogger(__filename);
  32. /**
  33. * DOM events for when full screen mode has changed. Different browsers need
  34. * different vendor prefixes.
  35. *
  36. * @private
  37. * @type {Array<string>}
  38. */
  39. const FULL_SCREEN_EVENTS = [
  40. 'webkitfullscreenchange',
  41. 'mozfullscreenchange',
  42. 'fullscreenchange'
  43. ];
  44. /**
  45. * The CSS class to apply to the root element of the conference so CSS can
  46. * modify the app layout.
  47. *
  48. * @private
  49. * @type {Object}
  50. */
  51. const LAYOUT_CLASSNAMES = {
  52. [LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'horizontal-filmstrip',
  53. [LAYOUTS.TILE_VIEW]: 'tile-view',
  54. [LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'vertical-filmstrip'
  55. };
  56. /**
  57. * The type of the React {@code Component} props of {@link Conference}.
  58. */
  59. type Props = {
  60. /**
  61. * Whether the local participant is recording the conference.
  62. */
  63. _iAmRecorder: boolean,
  64. /**
  65. * The CSS class to apply to the root of {@link Conference} to modify the
  66. * application layout.
  67. */
  68. _layoutClassName: string,
  69. /**
  70. * Conference room name.
  71. */
  72. _room: string,
  73. /**
  74. * Whether or not the current UI layout should be in tile view.
  75. */
  76. _shouldDisplayTileView: boolean,
  77. dispatch: Function,
  78. t: Function
  79. }
  80. /**
  81. * The conference page of the Web application.
  82. */
  83. class Conference extends Component<Props> {
  84. _onFullScreenChange: Function;
  85. _onShowToolbar: Function;
  86. _originalOnShowToolbar: Function;
  87. /**
  88. * Initializes a new Conference instance.
  89. *
  90. * @param {Object} props - The read-only properties with which the new
  91. * instance is to be initialized.
  92. */
  93. constructor(props) {
  94. super(props);
  95. // Throttle and bind this component's mousemove handler to prevent it
  96. // from firing too often.
  97. this._originalOnShowToolbar = this._onShowToolbar;
  98. this._onShowToolbar = _.throttle(
  99. () => this._originalOnShowToolbar(),
  100. 100,
  101. {
  102. leading: true,
  103. trailing: false
  104. });
  105. // Bind event handler so it is only bound once for every instance.
  106. this._onFullScreenChange = this._onFullScreenChange.bind(this);
  107. }
  108. /**
  109. * Start the connection and get the UI ready for the conference.
  110. *
  111. * @inheritdoc
  112. */
  113. componentDidMount() {
  114. const { configLocation } = config;
  115. if (configLocation) {
  116. obtainConfig(configLocation, this.props._room)
  117. .then(() => {
  118. const now = window.performance.now();
  119. APP.connectionTimes['configuration.fetched'] = now;
  120. logger.log('(TIME) configuration fetched:\t', now);
  121. this._start();
  122. })
  123. .catch(err => {
  124. logger.log(err);
  125. // Show obtain config error.
  126. APP.UI.messageHandler.showError({
  127. descriptionKey: 'dialog.connectError',
  128. titleKey: 'connection.CONNFAIL'
  129. });
  130. });
  131. } else {
  132. this._start();
  133. }
  134. }
  135. /**
  136. * Calls into legacy UI to update the application layout, if necessary.
  137. *
  138. * @inheritdoc
  139. * returns {void}
  140. */
  141. componentDidUpdate(prevProps) {
  142. if (this.props._shouldDisplayTileView
  143. === prevProps._shouldDisplayTileView) {
  144. return;
  145. }
  146. // TODO: For now VideoLayout is being called as LargeVideo and Filmstrip
  147. // sizing logic is still handled outside of React. Once all components
  148. // are in react they should calculate size on their own as much as
  149. // possible and pass down sizings.
  150. VideoLayout.refreshLayout();
  151. }
  152. /**
  153. * Disconnect from the conference when component will be
  154. * unmounted.
  155. *
  156. * @inheritdoc
  157. */
  158. componentWillUnmount() {
  159. APP.UI.unbindEvents();
  160. FULL_SCREEN_EVENTS.forEach(name =>
  161. document.removeEventListener(name, this._onFullScreenChange));
  162. APP.conference.isJoined() && this.props.dispatch(disconnect());
  163. }
  164. /**
  165. * Implements React's {@link Component#render()}.
  166. *
  167. * @inheritdoc
  168. * @returns {ReactElement}
  169. */
  170. render() {
  171. const {
  172. VIDEO_QUALITY_LABEL_DISABLED,
  173. // XXX The character casing of the name filmStripOnly utilized by
  174. // interfaceConfig is obsolete but legacy support is required.
  175. filmStripOnly: filmstripOnly
  176. } = interfaceConfig;
  177. const hideVideoQualityLabel
  178. = filmstripOnly
  179. || VIDEO_QUALITY_LABEL_DISABLED
  180. || this.props._iAmRecorder;
  181. return (
  182. <div
  183. className = { this.props._layoutClassName }
  184. id = 'videoconference_page'
  185. onMouseMove = { this._onShowToolbar }>
  186. <Notice />
  187. <div id = 'videospace'>
  188. <LargeVideo />
  189. { hideVideoQualityLabel
  190. || <Labels /> }
  191. <Filmstrip filmstripOnly = { filmstripOnly } />
  192. </div>
  193. { filmstripOnly || <Toolbox /> }
  194. { filmstripOnly || <Chat /> }
  195. <NotificationsContainer />
  196. <CalleeInfoContainer />
  197. </div>
  198. );
  199. }
  200. /**
  201. * Updates the Redux state when full screen mode has been enabled or
  202. * disabled.
  203. *
  204. * @private
  205. * @returns {void}
  206. */
  207. _onFullScreenChange() {
  208. this.props.dispatch(fullScreenChanged(APP.UI.isFullScreen()));
  209. }
  210. /**
  211. * Displays the toolbar.
  212. *
  213. * @private
  214. * @returns {void}
  215. */
  216. _onShowToolbar() {
  217. this.props.dispatch(showToolbox());
  218. }
  219. /**
  220. * Until we don't rewrite UI using react components
  221. * we use UI.start from old app. Also method translates
  222. * component right after it has been mounted.
  223. *
  224. * @inheritdoc
  225. */
  226. _start() {
  227. APP.UI.start();
  228. APP.UI.registerListeners();
  229. APP.UI.bindEvents();
  230. FULL_SCREEN_EVENTS.forEach(name =>
  231. document.addEventListener(name, this._onFullScreenChange));
  232. const { dispatch, t } = this.props;
  233. dispatch(connect());
  234. maybeShowSuboptimalExperienceNotification(dispatch, t);
  235. interfaceConfig.filmStripOnly
  236. && dispatch(setToolboxAlwaysVisible(true));
  237. }
  238. }
  239. /**
  240. * Maps (parts of) the Redux state to the associated props for the
  241. * {@code Conference} component.
  242. *
  243. * @param {Object} state - The Redux state.
  244. * @private
  245. * @returns {{
  246. * _iAmRecorder: boolean,
  247. * _layoutClassName: string,
  248. * _room: ?string,
  249. * _shouldDisplayTileView: boolean
  250. * }}
  251. */
  252. function _mapStateToProps(state) {
  253. const currentLayout = getCurrentLayout(state);
  254. return {
  255. _iAmRecorder: state['features/base/config'].iAmRecorder,
  256. _layoutClassName: LAYOUT_CLASSNAMES[currentLayout],
  257. _room: state['features/base/conference'].room,
  258. _shouldDisplayTileView: shouldDisplayTileView(state)
  259. };
  260. }
  261. export default reactReduxConnect(_mapStateToProps)(translate(Conference));