您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

ParticipantsPane.js 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { ThemeProvider } from 'styled-components';
  4. import { openDialog } from '../../../base/dialog';
  5. import { translate } from '../../../base/i18n';
  6. import { isLocalParticipantModerator } from '../../../base/participants';
  7. import { connect } from '../../../base/redux';
  8. import { MuteEveryoneDialog } from '../../../video-menu/components/';
  9. import { close } from '../../actions';
  10. import { classList, findStyledAncestor, getParticipantsPaneOpen } from '../../functions';
  11. import theme from '../../theme.json';
  12. import { FooterContextMenu } from '../FooterContextMenu';
  13. import { LobbyParticipantList } from './LobbyParticipantList';
  14. import { MeetingParticipantList } from './MeetingParticipantList';
  15. import {
  16. AntiCollapse,
  17. Close,
  18. Container,
  19. Footer,
  20. FooterButton,
  21. FooterEllipsisButton,
  22. FooterEllipsisContainer,
  23. Header
  24. } from './styled';
  25. /**
  26. * The type of the React {@code Component} props of {@link ParticipantsPane}.
  27. */
  28. type Props = {
  29. /**
  30. * Is the participants pane open.
  31. */
  32. _paneOpen: boolean,
  33. /**
  34. * Whether to show the footer menu.
  35. */
  36. _showFooter: boolean,
  37. /**
  38. * The Redux dispatch function.
  39. */
  40. dispatch: Function,
  41. /**
  42. * The i18n translate function.
  43. */
  44. t: Function
  45. };
  46. /**
  47. * The type of the React {@code Component} state of {@link ParticipantsPane}.
  48. */
  49. type State = {
  50. /**
  51. * Indicates if the footer context menu is open.
  52. */
  53. contextOpen: boolean,
  54. };
  55. /**
  56. * Implements the participants list.
  57. */
  58. class ParticipantsPane extends Component<Props, State> {
  59. /**
  60. * Initializes a new {@code ParticipantsPane} instance.
  61. *
  62. * @inheritdoc
  63. */
  64. constructor(props) {
  65. super(props);
  66. this.state = {
  67. contextOpen: false
  68. };
  69. // Bind event handlers so they are only bound once per instance.
  70. this._onClosePane = this._onClosePane.bind(this);
  71. this._onKeyPress = this._onKeyPress.bind(this);
  72. this._onMuteAll = this._onMuteAll.bind(this);
  73. this._onToggleContext = this._onToggleContext.bind(this);
  74. this._onWindowClickListener = this._onWindowClickListener.bind(this);
  75. }
  76. /**
  77. * Implements React's {@link Component#componentDidMount()}.
  78. *
  79. * @inheritdoc
  80. */
  81. componentDidMount() {
  82. window.addEventListener('click', this._onWindowClickListener);
  83. }
  84. /**
  85. * Implements React's {@link Component#componentWillUnmount()}.
  86. *
  87. * @inheritdoc
  88. */
  89. componentWillUnmount() {
  90. window.removeEventListener('click', this._onWindowClickListener);
  91. }
  92. /**
  93. * Implements React's {@link Component#render}.
  94. *
  95. * @inheritdoc
  96. */
  97. render() {
  98. const {
  99. _paneOpen,
  100. _showFooter,
  101. t
  102. } = this.props;
  103. // when the pane is not open optimize to not
  104. // execute the MeetingParticipantList render for large list of participants
  105. if (!_paneOpen) {
  106. return null;
  107. }
  108. return (
  109. <ThemeProvider theme = { theme }>
  110. <div className = { classList('participants_pane', !_paneOpen && 'participants_pane--closed') }>
  111. <div className = 'participants_pane-content'>
  112. <Header>
  113. <Close
  114. aria-label = { t('participantsPane.close', 'Close') }
  115. onClick = { this._onClosePane }
  116. onKeyPress = { this._onKeyPress }
  117. role = 'button'
  118. tabIndex = { 0 } />
  119. </Header>
  120. <Container>
  121. <LobbyParticipantList />
  122. <AntiCollapse />
  123. <MeetingParticipantList />
  124. </Container>
  125. {_showFooter && (
  126. <Footer>
  127. <FooterButton onClick = { this._onMuteAll }>
  128. {t('participantsPane.actions.muteAll')}
  129. </FooterButton>
  130. <FooterEllipsisContainer>
  131. <FooterEllipsisButton
  132. id = 'participants-pane-context-menu'
  133. onClick = { this._onToggleContext } />
  134. {this.state.contextOpen
  135. && <FooterContextMenu onMouseLeave = { this._onToggleContext } />}
  136. </FooterEllipsisContainer>
  137. </Footer>
  138. )}
  139. </div>
  140. </div>
  141. </ThemeProvider>
  142. );
  143. }
  144. _onClosePane: () => void;
  145. /**
  146. * Callback for closing the participant pane.
  147. *
  148. * @private
  149. * @returns {void}
  150. */
  151. _onClosePane() {
  152. this.props.dispatch(close());
  153. }
  154. _onKeyPress: (Object) => void;
  155. /**
  156. * KeyPress handler for accessibility for closing the participants pane.
  157. *
  158. * @param {Object} e - The key event to handle.
  159. *
  160. * @returns {void}
  161. */
  162. _onKeyPress(e) {
  163. if (e.key === ' ' || e.key === 'Enter') {
  164. e.preventDefault();
  165. this._onClosePane();
  166. }
  167. }
  168. _onMuteAll: () => void;
  169. /**
  170. * The handler for clicking mute all button.
  171. *
  172. * @returns {void}
  173. */
  174. _onMuteAll() {
  175. this.props.dispatch(openDialog(MuteEveryoneDialog));
  176. }
  177. _onToggleContext: () => void;
  178. /**
  179. * Handler for toggling open/close of the footer context menu.
  180. *
  181. * @returns {void}
  182. */
  183. _onToggleContext() {
  184. this.setState({
  185. contextOpen: !this.state.contextOpen
  186. });
  187. }
  188. _onWindowClickListener: (event: Object) => void;
  189. /**
  190. * Window click event listener.
  191. *
  192. * @param {Event} e - The click event.
  193. * @returns {void}
  194. */
  195. _onWindowClickListener(e) {
  196. if (this.state.contextOpen && !findStyledAncestor(e.target, FooterEllipsisContainer)) {
  197. this.setState({
  198. contextOpen: false
  199. });
  200. }
  201. }
  202. }
  203. /**
  204. * Maps (parts of) the redux state to the React {@code Component} props of
  205. * {@code ParticipantsPane}.
  206. *
  207. * @param {Object} state - The redux state.
  208. * @protected
  209. * @returns {{
  210. * _paneOpen: boolean,
  211. * _showFooter: boolean
  212. * }}
  213. */
  214. function _mapStateToProps(state: Object) {
  215. const isPaneOpen = getParticipantsPaneOpen(state);
  216. return {
  217. _paneOpen: isPaneOpen,
  218. _showFooter: isPaneOpen && isLocalParticipantModerator(state)
  219. };
  220. }
  221. export default translate(connect(_mapStateToProps)(ParticipantsPane));