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.tsx 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. import React from 'react';
  2. import { WithTranslation } from 'react-i18next';
  3. // @ts-expect-error
  4. import keyboardShortcut from '../../../../../modules/keyboardshortcut/keyboardshortcut';
  5. import AbstractDialogTab, {
  6. IProps as AbstractDialogTabProps
  7. } from '../../../base/dialog/components/web/AbstractDialogTab';
  8. import { translate } from '../../../base/i18n/functions';
  9. import Checkbox from '../../../base/ui/components/web/Checkbox';
  10. import Select from '../../../base/ui/components/web/Select';
  11. import { MAX_ACTIVE_PARTICIPANTS } from '../../../filmstrip/constants';
  12. import { SS_DEFAULT_FRAME_RATE } from '../../constants';
  13. /**
  14. * The type of the React {@code Component} props of {@link MoreTab}.
  15. */
  16. export type Props = AbstractDialogTabProps & WithTranslation & {
  17. /**
  18. * The currently selected desktop share frame rate in the frame rate select dropdown.
  19. */
  20. currentFramerate: string;
  21. /**
  22. * All available desktop capture frame rates.
  23. */
  24. desktopShareFramerates: Array<number>;
  25. /**
  26. * The types of enabled notifications that can be configured and their specific visibility.
  27. */
  28. enabledNotifications: Object;
  29. /**
  30. * Whether or not follow me is currently active (enabled by some other participant).
  31. */
  32. followMeActive: boolean;
  33. /**
  34. * The number of max participants to display on stage.
  35. */
  36. maxStageParticipants: number;
  37. /**
  38. * Whether or not to display moderator-only settings.
  39. */
  40. showModeratorSettings: boolean;
  41. /**
  42. * Whether or not to display notifications settings.
  43. */
  44. showNotificationsSettings: boolean;
  45. /**
  46. * Whether or not to show prejoin screen.
  47. */
  48. showPrejoinPage: boolean;
  49. /**
  50. * Whether or not to display the prejoin settings section.
  51. */
  52. showPrejoinSettings: boolean;
  53. /**
  54. * Wether or not the stage filmstrip is enabled.
  55. */
  56. stageFilmstripEnabled: boolean;
  57. /**
  58. * Invoked to obtain translated strings.
  59. */
  60. t: Function;
  61. };
  62. /**
  63. * React {@code Component} for modifying language and moderator settings.
  64. *
  65. * @augments Component
  66. */
  67. class MoreTab extends AbstractDialogTab<Props, any> {
  68. /**
  69. * Initializes a new {@code MoreTab} instance.
  70. *
  71. * @param {Object} props - The read-only properties with which the new
  72. * instance is to be initialized.
  73. */
  74. constructor(props: Props) {
  75. super(props);
  76. // Bind event handler so it is only bound once for every instance.
  77. this._onFramerateItemSelect = this._onFramerateItemSelect.bind(this);
  78. this._onEnabledNotificationsChanged = this._onEnabledNotificationsChanged.bind(this);
  79. this._onShowPrejoinPageChanged = this._onShowPrejoinPageChanged.bind(this);
  80. this._onKeyboardShortcutEnableChanged = this._onKeyboardShortcutEnableChanged.bind(this);
  81. this._renderMaxStageParticipantsSelect = this._renderMaxStageParticipantsSelect.bind(this);
  82. this._onMaxStageParticipantsSelect = this._onMaxStageParticipantsSelect.bind(this);
  83. }
  84. /**
  85. * Implements React's {@link Component#render()}.
  86. *
  87. * @inheritdoc
  88. * @returns {ReactElement}
  89. */
  90. render() {
  91. const content = [];
  92. content.push(this._renderSettingsLeft());
  93. content.push(this._renderSettingsRight());
  94. return (
  95. <div
  96. className = 'more-tab box'
  97. key = 'more'>
  98. { content }
  99. </div>
  100. );
  101. }
  102. /**
  103. * Callback invoked to select a frame rate from the select dropdown.
  104. *
  105. * @param {Object} e - The key event to handle.
  106. * @private
  107. * @returns {void}
  108. */
  109. _onFramerateItemSelect(e: React.ChangeEvent<HTMLSelectElement>) {
  110. const frameRate = e.target.value;
  111. super._onChange({ currentFramerate: frameRate });
  112. }
  113. /**
  114. * Callback invoked to select if the lobby
  115. * should be shown.
  116. *
  117. * @param {Object} e - The key event to handle.
  118. *
  119. * @returns {void}
  120. */
  121. _onShowPrejoinPageChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>) {
  122. super._onChange({ showPrejoinPage: checked });
  123. }
  124. /**
  125. * Callback invoked to select if the given type of
  126. * notifications should be shown.
  127. *
  128. * @param {Object} e - The key event to handle.
  129. * @param {string} type - The type of the notification.
  130. *
  131. * @returns {void}
  132. */
  133. _onEnabledNotificationsChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>, type: any) {
  134. super._onChange({
  135. enabledNotifications: {
  136. ...this.props.enabledNotifications,
  137. [type]: checked
  138. }
  139. });
  140. }
  141. /**
  142. * Callback invoked to select if global keyboard shortcuts
  143. * should be enabled.
  144. *
  145. * @param {Object} e - The key event to handle.
  146. *
  147. * @returns {void}
  148. */
  149. _onKeyboardShortcutEnableChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>) {
  150. keyboardShortcut.enable(checked);
  151. super._onChange({ keyboardShortcutEnable: checked });
  152. }
  153. /**
  154. * Callback invoked to select a max number of stage participants from the select dropdown.
  155. *
  156. * @param {Object} e - The key event to handle.
  157. * @private
  158. * @returns {void}
  159. */
  160. _onMaxStageParticipantsSelect(e: React.ChangeEvent<HTMLSelectElement>) {
  161. const maxParticipants = Number(e.target.value);
  162. super._onChange({ maxStageParticipants: maxParticipants });
  163. }
  164. /**
  165. * Returns the React Element for the desktop share frame rate dropdown.
  166. *
  167. * @returns {ReactElement}
  168. */
  169. _renderFramerateSelect() {
  170. const { currentFramerate, desktopShareFramerates, t } = this.props;
  171. const frameRateItems = desktopShareFramerates.map((frameRate: number) => {
  172. return {
  173. value: frameRate,
  174. label: `${frameRate} ${t('settings.framesPerSecond')}`
  175. };
  176. });
  177. return (
  178. <div
  179. className = 'settings-sub-pane-element'
  180. key = 'frameRate'>
  181. <div className = 'dropdown-menu'>
  182. <Select
  183. bottomLabel = { parseInt(currentFramerate, 10) > SS_DEFAULT_FRAME_RATE
  184. ? t('settings.desktopShareHighFpsWarning')
  185. : t('settings.desktopShareWarning') }
  186. label = { t('settings.desktopShareFramerate') }
  187. onChange = { this._onFramerateItemSelect }
  188. options = { frameRateItems }
  189. value = { currentFramerate } />
  190. </div>
  191. </div>
  192. );
  193. }
  194. /**
  195. * Returns the React Element for keyboardShortcut settings.
  196. *
  197. * @private
  198. * @returns {ReactElement}
  199. */
  200. _renderKeyboardShortcutCheckbox() {
  201. const { t } = this.props;
  202. return (
  203. <div
  204. className = 'settings-sub-pane-element'
  205. key = 'keyboard-shortcut'>
  206. <span className = 'checkbox-label'>
  207. { t('keyboardShortcuts.keyboardShortcuts') }
  208. </span>
  209. <Checkbox
  210. checked = { keyboardShortcut.getEnabled() }
  211. label = { t('prejoin.keyboardShortcuts') }
  212. name = 'enable-keyboard-shortcuts'
  213. onChange = { this._onKeyboardShortcutEnableChanged } />
  214. </div>
  215. );
  216. }
  217. /**
  218. * Returns the React Element for modifying prejoin screen settings.
  219. *
  220. * @private
  221. * @returns {ReactElement}
  222. */
  223. _renderPrejoinScreenSettings() {
  224. const { t, showPrejoinPage } = this.props;
  225. return (
  226. <div
  227. className = 'settings-sub-pane-element'
  228. key = 'prejoin-screen'>
  229. <span className = 'checkbox-label'>
  230. { t('prejoin.premeeting') }
  231. </span>
  232. <Checkbox
  233. checked = { showPrejoinPage }
  234. label = { t('prejoin.showScreen') }
  235. name = 'show-prejoin-page'
  236. onChange = { this._onShowPrejoinPageChanged } />
  237. </div>
  238. );
  239. }
  240. /**
  241. * Returns the React Element for modifying the enabled notifications settings.
  242. *
  243. * @private
  244. * @returns {ReactElement}
  245. */
  246. _renderNotificationsSettings() {
  247. const { t, enabledNotifications } = this.props;
  248. return (
  249. <div
  250. className = 'settings-sub-pane-element'
  251. key = 'notifications'>
  252. <span className = 'checkbox-label'>
  253. { t('notify.displayNotifications') }
  254. </span>
  255. {
  256. Object.keys(enabledNotifications).map(key => (
  257. <Checkbox
  258. checked = { Boolean(enabledNotifications[key as keyof typeof enabledNotifications]) }
  259. key = { key }
  260. label = { t(key) }
  261. name = { `show-${key}` }
  262. /* eslint-disable-next-line react/jsx-no-bind */
  263. onChange = { e => this._onEnabledNotificationsChanged(e, key) } />
  264. ))
  265. }
  266. </div>
  267. );
  268. }
  269. /**
  270. * Returns the React Element for the max stage participants dropdown.
  271. *
  272. * @returns {ReactElement}
  273. */
  274. _renderMaxStageParticipantsSelect() {
  275. const { maxStageParticipants, t, stageFilmstripEnabled } = this.props;
  276. if (!stageFilmstripEnabled) {
  277. return null;
  278. }
  279. const maxParticipantsItems = Array(MAX_ACTIVE_PARTICIPANTS).fill(0)
  280. .map((no, index) => {
  281. return {
  282. value: index + 1,
  283. label: `${index + 1}`
  284. };
  285. });
  286. return (
  287. <div
  288. className = 'settings-sub-pane-element'
  289. key = 'maxStageParticipants'>
  290. <div className = 'dropdown-menu'>
  291. <Select
  292. label = { t('settings.maxStageParticipants') }
  293. onChange = { this._onMaxStageParticipantsSelect }
  294. options = { maxParticipantsItems }
  295. value = { maxStageParticipants } />
  296. </div>
  297. </div>
  298. );
  299. }
  300. /**
  301. * Returns the React element that needs to be displayed on the right half of the more tabs.
  302. *
  303. * @private
  304. * @returns {ReactElement}
  305. */
  306. _renderSettingsRight() {
  307. return (
  308. <div
  309. className = 'settings-sub-pane right'
  310. key = 'settings-sub-pane-right'>
  311. { this._renderFramerateSelect() }
  312. { this._renderMaxStageParticipantsSelect() }
  313. </div>
  314. );
  315. }
  316. /**
  317. * Returns the React element that needs to be displayed on the left half of the more tabs.
  318. *
  319. * @returns {ReactElement}
  320. */
  321. _renderSettingsLeft() {
  322. const { showNotificationsSettings, showPrejoinSettings } = this.props;
  323. return (
  324. <div
  325. className = 'settings-sub-pane left'
  326. key = 'settings-sub-pane-left'>
  327. { showPrejoinSettings && this._renderPrejoinScreenSettings() }
  328. { showNotificationsSettings && this._renderNotificationsSettings() }
  329. { this._renderKeyboardShortcutCheckbox() }
  330. </div>
  331. );
  332. }
  333. }
  334. export default translate(MoreTab);