Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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. * The currently selected language to display in the language select
  23. * dropdown.
  24. */
  25. currentLanguage: string;
  26. /**
  27. * All available desktop capture frame rates.
  28. */
  29. desktopShareFramerates: Array<number>;
  30. /**
  31. * Whether to show hide self view setting.
  32. */
  33. disableHideSelfView: boolean;
  34. /**
  35. * The types of enabled notifications that can be configured and their specific visibility.
  36. */
  37. enabledNotifications: Object;
  38. /**
  39. * Whether or not follow me is currently active (enabled by some other participant).
  40. */
  41. followMeActive: boolean;
  42. /**
  43. * Whether or not to hide self-view screen.
  44. */
  45. hideSelfView: boolean;
  46. /**
  47. * All available languages to display in the language select dropdown.
  48. */
  49. languages: Array<string>;
  50. /**
  51. * The number of max participants to display on stage.
  52. */
  53. maxStageParticipants: number;
  54. /**
  55. * Whether or not to display the language select dropdown.
  56. */
  57. showLanguageSettings: boolean;
  58. /**
  59. * Whether or not to display moderator-only settings.
  60. */
  61. showModeratorSettings: boolean;
  62. /**
  63. * Whether or not to display notifications settings.
  64. */
  65. showNotificationsSettings: boolean;
  66. /**
  67. * Whether or not to show prejoin screen.
  68. */
  69. showPrejoinPage: boolean;
  70. /**
  71. * Whether or not to display the prejoin settings section.
  72. */
  73. showPrejoinSettings: boolean;
  74. /**
  75. * Wether or not the stage filmstrip is enabled.
  76. */
  77. stageFilmstripEnabled: boolean;
  78. /**
  79. * Invoked to obtain translated strings.
  80. */
  81. t: Function;
  82. };
  83. /**
  84. * React {@code Component} for modifying language and moderator settings.
  85. *
  86. * @augments Component
  87. */
  88. class MoreTab extends AbstractDialogTab<Props, {}> {
  89. /**
  90. * Initializes a new {@code MoreTab} instance.
  91. *
  92. * @param {Object} props - The read-only properties with which the new
  93. * instance is to be initialized.
  94. */
  95. constructor(props: Props) {
  96. super(props);
  97. // Bind event handler so it is only bound once for every instance.
  98. this._onFramerateItemSelect = this._onFramerateItemSelect.bind(this);
  99. this._onLanguageItemSelect = this._onLanguageItemSelect.bind(this);
  100. this._onEnabledNotificationsChanged = this._onEnabledNotificationsChanged.bind(this);
  101. this._onShowPrejoinPageChanged = this._onShowPrejoinPageChanged.bind(this);
  102. this._onKeyboardShortcutEnableChanged = this._onKeyboardShortcutEnableChanged.bind(this);
  103. this._onHideSelfViewChanged = this._onHideSelfViewChanged.bind(this);
  104. this._renderMaxStageParticipantsSelect = this._renderMaxStageParticipantsSelect.bind(this);
  105. this._onMaxStageParticipantsSelect = this._onMaxStageParticipantsSelect.bind(this);
  106. }
  107. /**
  108. * Implements React's {@link Component#render()}.
  109. *
  110. * @inheritdoc
  111. * @returns {ReactElement}
  112. */
  113. render() {
  114. const content = [];
  115. content.push(this._renderSettingsLeft());
  116. content.push(this._renderSettingsRight());
  117. return (
  118. <div
  119. className = 'more-tab box'
  120. key = 'more'>
  121. { content }
  122. </div>
  123. );
  124. }
  125. /**
  126. * Callback invoked to select a frame rate from the select dropdown.
  127. *
  128. * @param {Object} e - The key event to handle.
  129. * @private
  130. * @returns {void}
  131. */
  132. _onFramerateItemSelect(e: React.ChangeEvent<HTMLSelectElement>) {
  133. const frameRate = e.target.value;
  134. super._onChange({ currentFramerate: frameRate });
  135. }
  136. /**
  137. * Callback invoked to select a language from select dropdown.
  138. *
  139. * @param {Object} e - The key event to handle.
  140. *
  141. * @returns {void}
  142. */
  143. _onLanguageItemSelect(e: React.ChangeEvent<HTMLSelectElement>) {
  144. const language = e.target.value;
  145. super._onChange({ currentLanguage: language });
  146. }
  147. /**
  148. * Callback invoked to select if the lobby
  149. * should be shown.
  150. *
  151. * @param {Object} e - The key event to handle.
  152. *
  153. * @returns {void}
  154. */
  155. _onShowPrejoinPageChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>) {
  156. super._onChange({ showPrejoinPage: checked });
  157. }
  158. /**
  159. * Callback invoked to select if the given type of
  160. * notifications should be shown.
  161. *
  162. * @param {Object} e - The key event to handle.
  163. * @param {string} type - The type of the notification.
  164. *
  165. * @returns {void}
  166. */
  167. _onEnabledNotificationsChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>, type: any) {
  168. super._onChange({
  169. enabledNotifications: {
  170. ...this.props.enabledNotifications,
  171. [type]: checked
  172. }
  173. });
  174. }
  175. /**
  176. * Callback invoked to select if global keyboard shortcuts
  177. * should be enabled.
  178. *
  179. * @param {Object} e - The key event to handle.
  180. *
  181. * @returns {void}
  182. */
  183. _onKeyboardShortcutEnableChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>) {
  184. keyboardShortcut.enable(checked);
  185. super._onChange({ keyboardShortcutEnable: checked });
  186. }
  187. /**
  188. * Callback invoked to select if hide self view should be enabled.
  189. *
  190. * @param {Object} e - The key event to handle.
  191. *
  192. * @returns {void}
  193. */
  194. _onHideSelfViewChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>) {
  195. super._onChange({ hideSelfView: checked });
  196. }
  197. /**
  198. * Callback invoked to select a max number of stage participants from the select dropdown.
  199. *
  200. * @param {Object} e - The key event to handle.
  201. * @private
  202. * @returns {void}
  203. */
  204. _onMaxStageParticipantsSelect(e: React.ChangeEvent<HTMLSelectElement>) {
  205. const maxParticipants = Number(e.target.value);
  206. super._onChange({ maxStageParticipants: maxParticipants });
  207. }
  208. /**
  209. * Returns the React Element for the desktop share frame rate dropdown.
  210. *
  211. * @returns {ReactElement}
  212. */
  213. _renderFramerateSelect() {
  214. const { currentFramerate, desktopShareFramerates, t } = this.props;
  215. const frameRateItems = desktopShareFramerates.map((frameRate: number) => {
  216. return {
  217. value: frameRate,
  218. label: `${frameRate} ${t('settings.framesPerSecond')}`
  219. };
  220. });
  221. return (
  222. <div
  223. className = 'settings-sub-pane-element'
  224. key = 'frameRate'>
  225. <div className = 'dropdown-menu'>
  226. <Select
  227. bottomLabel = { parseInt(currentFramerate, 10) > SS_DEFAULT_FRAME_RATE
  228. ? t('settings.desktopShareHighFpsWarning')
  229. : t('settings.desktopShareWarning') }
  230. label = { t('settings.desktopShareFramerate') }
  231. onChange = { this._onFramerateItemSelect }
  232. options = { frameRateItems }
  233. value = { currentFramerate } />
  234. </div>
  235. </div>
  236. );
  237. }
  238. /**
  239. * Returns the React Element for keyboardShortcut settings.
  240. *
  241. * @private
  242. * @returns {ReactElement}
  243. */
  244. _renderKeyboardShortcutCheckbox() {
  245. const { t } = this.props;
  246. return (
  247. <div
  248. className = 'settings-sub-pane-element'
  249. key = 'keyboard-shortcut'>
  250. <span className = 'checkbox-label'>
  251. { t('keyboardShortcuts.keyboardShortcuts') }
  252. </span>
  253. <Checkbox
  254. checked = { keyboardShortcut.getEnabled() }
  255. label = { t('prejoin.keyboardShortcuts') }
  256. name = 'enable-keyboard-shortcuts'
  257. onChange = { this._onKeyboardShortcutEnableChanged } />
  258. </div>
  259. );
  260. }
  261. /**
  262. * Returns the React Element for self view setting.
  263. *
  264. * @private
  265. * @returns {ReactElement}
  266. */
  267. _renderSelfViewCheckbox() {
  268. const { hideSelfView, t } = this.props;
  269. return (
  270. <div
  271. className = 'settings-sub-pane-element'
  272. key = 'selfview'>
  273. <span className = 'checkbox-label'>
  274. { t('settings.selfView') }
  275. </span>
  276. <Checkbox
  277. checked = { hideSelfView }
  278. label = { t('videothumbnail.hideSelfView') }
  279. name = 'hide-self-view'
  280. onChange = { this._onHideSelfViewChanged } />
  281. </div>
  282. );
  283. }
  284. /**
  285. * Returns the menu item for changing displayed language.
  286. *
  287. * @private
  288. * @returns {ReactElement}
  289. */
  290. _renderLanguageSelect() {
  291. const {
  292. currentLanguage,
  293. languages,
  294. t
  295. } = this.props;
  296. const languageItems
  297. = languages.map((language: string) => {
  298. return {
  299. value: language,
  300. label: t(`languages:${language}`)
  301. };
  302. });
  303. return (
  304. <div
  305. className = 'settings-sub-pane-element'
  306. key = 'language'>
  307. <div className = 'dropdown-menu'>
  308. <Select
  309. label = { t('settings.language') }
  310. onChange = { this._onLanguageItemSelect }
  311. options = { languageItems }
  312. value = { currentLanguage } />
  313. </div>
  314. </div>
  315. );
  316. }
  317. /**
  318. * Returns the React Element for modifying prejoin screen settings.
  319. *
  320. * @private
  321. * @returns {ReactElement}
  322. */
  323. _renderPrejoinScreenSettings() {
  324. const { t, showPrejoinPage } = this.props;
  325. return (
  326. <div
  327. className = 'settings-sub-pane-element'
  328. key = 'prejoin-screen'>
  329. <span className = 'checkbox-label'>
  330. { t('prejoin.premeeting') }
  331. </span>
  332. <Checkbox
  333. checked = { showPrejoinPage }
  334. label = { t('prejoin.showScreen') }
  335. name = 'show-prejoin-page'
  336. onChange = { this._onShowPrejoinPageChanged } />
  337. </div>
  338. );
  339. }
  340. /**
  341. * Returns the React Element for modifying the enabled notifications settings.
  342. *
  343. * @private
  344. * @returns {ReactElement}
  345. */
  346. _renderNotificationsSettings() {
  347. const { t, enabledNotifications } = this.props;
  348. return (
  349. <div
  350. className = 'settings-sub-pane-element'
  351. key = 'notifications'>
  352. <span className = 'checkbox-label'>
  353. { t('notify.displayNotifications') }
  354. </span>
  355. {
  356. Object.keys(enabledNotifications).map(key => (
  357. <Checkbox
  358. checked = { Boolean(enabledNotifications[key as keyof typeof enabledNotifications]) }
  359. key = { key }
  360. label = { t(key) }
  361. name = { `show-${key}` }
  362. /* eslint-disable-next-line react/jsx-no-bind */
  363. onChange = { e => this._onEnabledNotificationsChanged(e, key) } />
  364. ))
  365. }
  366. </div>
  367. );
  368. }
  369. /**
  370. * Returns the React Element for the max stage participants dropdown.
  371. *
  372. * @returns {ReactElement}
  373. */
  374. _renderMaxStageParticipantsSelect() {
  375. const { maxStageParticipants, t, stageFilmstripEnabled } = this.props;
  376. if (!stageFilmstripEnabled) {
  377. return null;
  378. }
  379. const maxParticipantsItems = Array(MAX_ACTIVE_PARTICIPANTS).fill(0)
  380. .map((no, index) => {
  381. return {
  382. value: index + 1,
  383. label: `${index + 1}`
  384. };
  385. });
  386. return (
  387. <div
  388. className = 'settings-sub-pane-element'
  389. key = 'maxStageParticipants'>
  390. <div className = 'dropdown-menu'>
  391. <Select
  392. label = { t('settings.maxStageParticipants') }
  393. onChange = { this._onMaxStageParticipantsSelect }
  394. options = { maxParticipantsItems }
  395. value = { maxStageParticipants } />
  396. </div>
  397. </div>
  398. );
  399. }
  400. /**
  401. * Returns the React element that needs to be displayed on the right half of the more tabs.
  402. *
  403. * @private
  404. * @returns {ReactElement}
  405. */
  406. _renderSettingsRight() {
  407. const { showLanguageSettings } = this.props;
  408. return (
  409. <div
  410. className = 'settings-sub-pane right'
  411. key = 'settings-sub-pane-right'>
  412. { showLanguageSettings && this._renderLanguageSelect() }
  413. { this._renderFramerateSelect() }
  414. { this._renderMaxStageParticipantsSelect() }
  415. </div>
  416. );
  417. }
  418. /**
  419. * Returns the React element that needs to be displayed on the left half of the more tabs.
  420. *
  421. * @returns {ReactElement}
  422. */
  423. _renderSettingsLeft() {
  424. const { disableHideSelfView, showNotificationsSettings, showPrejoinSettings } = this.props;
  425. return (
  426. <div
  427. className = 'settings-sub-pane left'
  428. key = 'settings-sub-pane-left'>
  429. { showPrejoinSettings && this._renderPrejoinScreenSettings() }
  430. { showNotificationsSettings && this._renderNotificationsSettings() }
  431. { this._renderKeyboardShortcutCheckbox() }
  432. { !disableHideSelfView && this._renderSelfViewCheckbox() }
  433. </div>
  434. );
  435. }
  436. }
  437. export default translate(MoreTab);