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.

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