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.web.js 6.8KB

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