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.

MoreTab.js 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. // @flow
  2. import { Checkbox } from '@atlaskit/checkbox';
  3. import DropdownMenu, {
  4. DropdownItem,
  5. DropdownItemGroup
  6. } from '@atlaskit/dropdown-menu';
  7. import React from 'react';
  8. import keyboardShortcut from '../../../../../modules/keyboardshortcut/keyboardshortcut';
  9. import { AbstractDialogTab } from '../../../base/dialog';
  10. import type { Props as AbstractDialogTabProps } from '../../../base/dialog';
  11. import { translate } from '../../../base/i18n';
  12. import TouchmoveHack from '../../../chat/components/web/TouchmoveHack';
  13. import { MAX_ACTIVE_PARTICIPANTS } from '../../../filmstrip';
  14. import { SS_DEFAULT_FRAME_RATE } from '../../constants';
  15. /**
  16. * The type of the React {@code Component} props of {@link MoreTab}.
  17. */
  18. export type Props = {
  19. ...$Exact<AbstractDialogTabProps>,
  20. /**
  21. * The currently selected desktop share frame rate in the frame rate select dropdown.
  22. */
  23. currentFramerate: string,
  24. /**
  25. * The currently selected language to display in the language select
  26. * dropdown.
  27. */
  28. currentLanguage: string,
  29. /**
  30. * All available desktop capture frame rates.
  31. */
  32. desktopShareFramerates: Array<number>,
  33. /**
  34. * Whether to show hide self view setting.
  35. */
  36. disableHideSelfView: boolean,
  37. /**
  38. * Whether or not follow me is currently active (enabled by some other participant).
  39. */
  40. followMeActive: boolean,
  41. /**
  42. * All available languages to display in the language select dropdown.
  43. */
  44. languages: Array<string>,
  45. /**
  46. * The types of enabled notifications that can be configured and their specific visibility.
  47. */
  48. enabledNotifications: Object,
  49. /**
  50. * Whether or not to display the language select dropdown.
  51. */
  52. showLanguageSettings: boolean,
  53. /**
  54. * Whether or not to display moderator-only settings.
  55. */
  56. showModeratorSettings: boolean,
  57. /**
  58. * Whether or not to display notifications settings.
  59. */
  60. showNotificationsSettings: boolean,
  61. /**
  62. * Whether or not to display the prejoin settings section.
  63. */
  64. showPrejoinSettings: boolean,
  65. /**
  66. * Whether or not to show prejoin screen.
  67. */
  68. showPrejoinPage: boolean,
  69. /**
  70. * Whether or not to hide self-view screen.
  71. */
  72. hideSelfView: boolean,
  73. /**
  74. * Invoked to obtain translated strings.
  75. */
  76. t: Function
  77. };
  78. /**
  79. * The type of the React {@code Component} state of {@link MoreTab}.
  80. */
  81. type State = {
  82. /**
  83. * Whether or not the desktop share frame rate select dropdown is open.
  84. */
  85. isFramerateSelectOpen: boolean,
  86. /**
  87. * Whether or not the language select dropdown is open.
  88. */
  89. isLanguageSelectOpen: boolean
  90. };
  91. /**
  92. * React {@code Component} for modifying language and moderator settings.
  93. *
  94. * @augments Component
  95. */
  96. class MoreTab extends AbstractDialogTab<Props, State> {
  97. /**
  98. * Initializes a new {@code MoreTab} instance.
  99. *
  100. * @param {Object} props - The read-only properties with which the new
  101. * instance is to be initialized.
  102. */
  103. constructor(props: Props) {
  104. super(props);
  105. this.state = {
  106. isFramerateSelectOpen: false,
  107. isLanguageSelectOpen: false,
  108. isMaxStageParticipantsOpen: false
  109. };
  110. // Bind event handler so it is only bound once for every instance.
  111. this._onFramerateDropdownOpenChange = this._onFramerateDropdownOpenChange.bind(this);
  112. this._onFramerateItemSelect = this._onFramerateItemSelect.bind(this);
  113. this._onLanguageDropdownOpenChange = this._onLanguageDropdownOpenChange.bind(this);
  114. this._onLanguageItemSelect = this._onLanguageItemSelect.bind(this);
  115. this._onEnabledNotificationsChanged = this._onEnabledNotificationsChanged.bind(this);
  116. this._onShowPrejoinPageChanged = this._onShowPrejoinPageChanged.bind(this);
  117. this._onKeyboardShortcutEnableChanged = this._onKeyboardShortcutEnableChanged.bind(this);
  118. this._onHideSelfViewChanged = this._onHideSelfViewChanged.bind(this);
  119. this._renderMaxStageParticipantsSelect = this._renderMaxStageParticipantsSelect.bind(this);
  120. this._onMaxStageParticipantsSelect = this._onMaxStageParticipantsSelect.bind(this);
  121. this._onMaxStageParticipantsOpenChange = this._onMaxStageParticipantsOpenChange.bind(this);
  122. }
  123. /**
  124. * Implements React's {@link Component#render()}.
  125. *
  126. * @inheritdoc
  127. * @returns {ReactElement}
  128. */
  129. render() {
  130. const content = [];
  131. content.push(this._renderSettingsLeft());
  132. content.push(this._renderSettingsRight());
  133. return (
  134. <div
  135. className = 'more-tab box'
  136. key = 'more'>
  137. { content }
  138. </div>
  139. );
  140. }
  141. _onFramerateDropdownOpenChange: (Object) => void;
  142. /**
  143. * Callback invoked to toggle display of the desktop share framerate select dropdown.
  144. *
  145. * @param {Object} event - The event for opening or closing the dropdown.
  146. * @private
  147. * @returns {void}
  148. */
  149. _onFramerateDropdownOpenChange({ isOpen }) {
  150. this.setState({ isFramerateSelectOpen: isOpen });
  151. }
  152. _onFramerateItemSelect: (Object) => void;
  153. /**
  154. * Callback invoked to select a frame rate from the select dropdown.
  155. *
  156. * @param {Object} e - The key event to handle.
  157. * @private
  158. * @returns {void}
  159. */
  160. _onFramerateItemSelect(e) {
  161. const frameRate = e.currentTarget.getAttribute('data-framerate');
  162. super._onChange({ currentFramerate: frameRate });
  163. }
  164. _onLanguageDropdownOpenChange: (Object) => void;
  165. /**
  166. * Callback invoked to toggle display of the language select dropdown.
  167. *
  168. * @param {Object} event - The event for opening or closing the dropdown.
  169. * @private
  170. * @returns {void}
  171. */
  172. _onLanguageDropdownOpenChange({ isOpen }) {
  173. this.setState({ isLanguageSelectOpen: isOpen });
  174. }
  175. _onLanguageItemSelect: (Object) => void;
  176. /**
  177. * Callback invoked to select a language from select dropdown.
  178. *
  179. * @param {Object} e - The key event to handle.
  180. *
  181. * @returns {void}
  182. */
  183. _onLanguageItemSelect(e) {
  184. const language = e.currentTarget.getAttribute('data-language');
  185. super._onChange({ currentLanguage: language });
  186. }
  187. _onShowPrejoinPageChanged: (Object) => void;
  188. /**
  189. * Callback invoked to select if the lobby
  190. * should be shown.
  191. *
  192. * @param {Object} e - The key event to handle.
  193. *
  194. * @returns {void}
  195. */
  196. _onShowPrejoinPageChanged({ target: { checked } }) {
  197. super._onChange({ showPrejoinPage: checked });
  198. }
  199. _onKeyboardShortcutEnableChanged: (Object) => void;
  200. /**
  201. * Callback invoked to select if the given type of
  202. * notifications should be shown.
  203. *
  204. * @param {Object} e - The key event to handle.
  205. * @param {string} type - The type of the notification.
  206. *
  207. * @returns {void}
  208. */
  209. _onEnabledNotificationsChanged({ target: { checked } }, type) {
  210. super._onChange({
  211. enabledNotifications: {
  212. ...this.props.enabledNotifications,
  213. [type]: checked
  214. }
  215. });
  216. }
  217. _onEnabledNotificationsChanged: (Object, string) => void;
  218. /**
  219. * Callback invoked to select if global keyboard shortcuts
  220. * should be enabled.
  221. *
  222. * @param {Object} e - The key event to handle.
  223. *
  224. * @returns {void}
  225. */
  226. _onKeyboardShortcutEnableChanged({ target: { checked } }) {
  227. keyboardShortcut.enable(checked);
  228. super._onChange({ keyboardShortcutEnable: checked });
  229. }
  230. _onHideSelfViewChanged: (Object) => void;
  231. /**
  232. * Callback invoked to select if hide self view should be enabled.
  233. *
  234. * @param {Object} e - The key event to handle.
  235. *
  236. * @returns {void}
  237. */
  238. _onHideSelfViewChanged({ target: { checked } }) {
  239. super._onChange({ hideSelfView: checked });
  240. }
  241. _onMaxStageParticipantsOpenChange: (Object) => void;
  242. /**
  243. * Callback invoked to toggle display of the max stage participants select dropdown.
  244. *
  245. * @param {Object} event - The event for opening or closing the dropdown.
  246. * @private
  247. * @returns {void}
  248. */
  249. _onMaxStageParticipantsOpenChange({ isOpen }) {
  250. this.setState({ isMaxStageParticipantsOpen: isOpen });
  251. }
  252. _onMaxStageParticipantsSelect: (Object) => void;
  253. /**
  254. * Callback invoked to select a max number of stage participants from the select dropdown.
  255. *
  256. * @param {Object} e - The key event to handle.
  257. * @private
  258. * @returns {void}
  259. */
  260. _onMaxStageParticipantsSelect(e) {
  261. const maxParticipants = e.currentTarget.getAttribute('data-maxparticipants');
  262. super._onChange({ maxStageParticipants: maxParticipants });
  263. }
  264. /**
  265. * Returns the React Element for the desktop share frame rate dropdown.
  266. *
  267. * @returns {ReactElement}
  268. */
  269. _renderFramerateSelect() {
  270. const { currentFramerate, desktopShareFramerates, t } = this.props;
  271. const frameRateItems = desktopShareFramerates.map(frameRate => (
  272. <DropdownItem
  273. data-framerate = { frameRate }
  274. key = { frameRate }
  275. onClick = { this._onFramerateItemSelect }>
  276. { `${frameRate} ${t('settings.framesPerSecond')}` }
  277. </DropdownItem>));
  278. return (
  279. <div
  280. className = 'settings-sub-pane-element'
  281. key = 'frameRate'>
  282. <h2 className = 'mock-atlaskit-label'>
  283. { t('settings.desktopShareFramerate') }
  284. </h2>
  285. <div className = 'dropdown-menu'>
  286. <TouchmoveHack
  287. flex = { true }
  288. isModal = { true }>
  289. <DropdownMenu
  290. isOpen = { this.state.isFramerateSelectOpen }
  291. onOpenChange = { this._onFramerateDropdownOpenChange }
  292. shouldFitContainer = { true }
  293. trigger = { currentFramerate
  294. ? `${currentFramerate} ${t('settings.framesPerSecond')}`
  295. : '' }
  296. triggerButtonProps = {{
  297. shouldFitContainer: true
  298. }}
  299. triggerType = 'button'>
  300. <DropdownItemGroup>
  301. { frameRateItems }
  302. </DropdownItemGroup>
  303. </DropdownMenu>
  304. </TouchmoveHack>
  305. </div>
  306. <div
  307. className = 'mock-atlaskit-label'>
  308. { parseInt(currentFramerate, 10) > SS_DEFAULT_FRAME_RATE
  309. ? t('settings.desktopShareHighFpsWarning')
  310. : t('settings.desktopShareWarning') }
  311. </div>
  312. </div>
  313. );
  314. }
  315. /**
  316. * Returns the React Element for keyboardShortcut settings.
  317. *
  318. * @private
  319. * @returns {ReactElement}
  320. */
  321. _renderKeyboardShortcutCheckbox() {
  322. const { t } = this.props;
  323. return (
  324. <div
  325. className = 'settings-sub-pane-element'
  326. key = 'keyboard-shortcut'>
  327. <h2 className = 'mock-atlaskit-label'>
  328. { t('keyboardShortcuts.keyboardShortcuts') }
  329. </h2>
  330. <Checkbox
  331. isChecked = { keyboardShortcut.getEnabled() }
  332. label = { t('prejoin.keyboardShortcuts') }
  333. name = 'enable-keyboard-shortcuts'
  334. onChange = { this._onKeyboardShortcutEnableChanged } />
  335. </div>
  336. );
  337. }
  338. /**
  339. * Returns the React Element for self view setting.
  340. *
  341. * @private
  342. * @returns {ReactElement}
  343. */
  344. _renderSelfViewCheckbox() {
  345. const { hideSelfView, t } = this.props;
  346. return (
  347. <div
  348. className = 'settings-sub-pane-element'
  349. key = 'selfview'>
  350. <h2 className = 'mock-atlaskit-label'>
  351. { t('settings.selfView') }
  352. </h2>
  353. <Checkbox
  354. isChecked = { hideSelfView }
  355. label = { t('videothumbnail.hideSelfView') }
  356. name = 'hide-self-view'
  357. onChange = { this._onHideSelfViewChanged } />
  358. </div>
  359. );
  360. }
  361. /**
  362. * Returns the menu item for changing displayed language.
  363. *
  364. * @private
  365. * @returns {ReactElement}
  366. */
  367. _renderLanguageSelect() {
  368. const {
  369. currentLanguage,
  370. languages,
  371. t
  372. } = this.props;
  373. const languageItems
  374. = languages.map(language => (
  375. <DropdownItem
  376. data-language = { language }
  377. key = { language }
  378. onClick = { this._onLanguageItemSelect }>
  379. { t(`languages:${language}`) }
  380. </DropdownItem>));
  381. return (
  382. <div
  383. className = 'settings-sub-pane-element'
  384. key = 'language'>
  385. <h2 className = 'mock-atlaskit-label'>
  386. { t('settings.language') }
  387. </h2>
  388. <div className = 'dropdown-menu'>
  389. <TouchmoveHack
  390. flex = { true }
  391. isModal = { true }>
  392. <DropdownMenu
  393. isOpen = { this.state.isLanguageSelectOpen }
  394. onOpenChange = { this._onLanguageDropdownOpenChange }
  395. shouldFitContainer = { true }
  396. trigger = { currentLanguage
  397. ? t(`languages:${currentLanguage}`)
  398. : '' }
  399. triggerButtonProps = {{
  400. shouldFitContainer: true
  401. }}
  402. triggerType = 'button'>
  403. <DropdownItemGroup>
  404. { languageItems }
  405. </DropdownItemGroup>
  406. </DropdownMenu>
  407. </TouchmoveHack>
  408. </div>
  409. </div>
  410. );
  411. }
  412. /**
  413. * Returns the React Element for modifying prejoin screen settings.
  414. *
  415. * @private
  416. * @returns {ReactElement}
  417. */
  418. _renderPrejoinScreenSettings() {
  419. const { t, showPrejoinPage } = this.props;
  420. return (
  421. <div
  422. className = 'settings-sub-pane-element'
  423. key = 'prejoin-screen'>
  424. <h2 className = 'mock-atlaskit-label'>
  425. { t('prejoin.premeeting') }
  426. </h2>
  427. <Checkbox
  428. isChecked = { showPrejoinPage }
  429. label = { t('prejoin.showScreen') }
  430. name = 'show-prejoin-page'
  431. onChange = { this._onShowPrejoinPageChanged } />
  432. </div>
  433. );
  434. }
  435. /**
  436. * Returns the React Element for modifying the enabled notifications settings.
  437. *
  438. * @private
  439. * @returns {ReactElement}
  440. */
  441. _renderNotificationsSettings() {
  442. const { t, enabledNotifications } = this.props;
  443. return (
  444. <div
  445. className = 'settings-sub-pane-element'
  446. key = 'notifications'>
  447. <h2 className = 'mock-atlaskit-label'>
  448. { t('notify.displayNotifications') }
  449. </h2>
  450. {
  451. Object.keys(enabledNotifications).map(key => (
  452. <Checkbox
  453. isChecked = { enabledNotifications[key] }
  454. key = { key }
  455. label = { t(key) }
  456. name = { `show-${key}` }
  457. /* eslint-disable-next-line react/jsx-no-bind */
  458. onChange = { e => this._onEnabledNotificationsChanged(e, key) } />
  459. ))
  460. }
  461. </div>
  462. );
  463. }
  464. _renderMaxStageParticipantsSelect: () => void;
  465. /**
  466. * Returns the React Element for the max stage participants dropdown.
  467. *
  468. * @returns {ReactElement}
  469. */
  470. _renderMaxStageParticipantsSelect() {
  471. const { maxStageParticipants, t } = this.props;
  472. const maxParticipantsItems = Array(MAX_ACTIVE_PARTICIPANTS).fill(0)
  473. .map((no, index) => (
  474. <DropdownItem
  475. data-maxparticipants = { index + 1 }
  476. key = { index + 1 }
  477. onClick = { this._onMaxStageParticipantsSelect }>
  478. {index + 1}
  479. </DropdownItem>));
  480. return (
  481. <div
  482. className = 'settings-sub-pane-element'
  483. key = 'maxStageParticipants'>
  484. <h2 className = 'mock-atlaskit-label'>
  485. { t('settings.maxStageParticipants') }
  486. </h2>
  487. <div className = 'dropdown-menu'>
  488. <TouchmoveHack
  489. flex = { true }
  490. isModal = { true }>
  491. <DropdownMenu
  492. isOpen = { this.state.isMaxStageParticipantsOpen }
  493. onOpenChange = { this._onMaxStageParticipantsOpenChange }
  494. shouldFitContainer = { true }
  495. trigger = { maxStageParticipants }
  496. triggerButtonProps = {{
  497. shouldFitContainer: true
  498. }}
  499. triggerType = 'button'>
  500. <DropdownItemGroup>
  501. { maxParticipantsItems }
  502. </DropdownItemGroup>
  503. </DropdownMenu>
  504. </TouchmoveHack>
  505. </div>
  506. </div>
  507. );
  508. }
  509. /**
  510. * Returns the React element that needs to be displayed on the right half of the more tabs.
  511. *
  512. * @private
  513. * @returns {ReactElement}
  514. */
  515. _renderSettingsRight() {
  516. const { showLanguageSettings } = this.props;
  517. return (
  518. <div
  519. className = 'settings-sub-pane right'
  520. key = 'settings-sub-pane-right'>
  521. { showLanguageSettings && this._renderLanguageSelect() }
  522. { this._renderFramerateSelect() }
  523. { this._renderMaxStageParticipantsSelect() }
  524. </div>
  525. );
  526. }
  527. /**
  528. * Returns the React element that needs to be displayed on the left half of the more tabs.
  529. *
  530. * @returns {ReactElement}
  531. */
  532. _renderSettingsLeft() {
  533. const { disableHideSelfView, showNotificationsSettings, showPrejoinSettings } = this.props;
  534. return (
  535. <div
  536. className = 'settings-sub-pane left'
  537. key = 'settings-sub-pane-left'>
  538. { showPrejoinSettings && this._renderPrejoinScreenSettings() }
  539. { showNotificationsSettings && this._renderNotificationsSettings() }
  540. { this._renderKeyboardShortcutCheckbox() }
  541. { !disableHideSelfView && this._renderSelfViewCheckbox() }
  542. </div>
  543. );
  544. }
  545. }
  546. export default translate(MoreTab);