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.

SettingsDialog.tsx 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. import React from 'react';
  2. import { connect } from 'react-redux';
  3. import { makeStyles } from 'tss-react/mui';
  4. import { IReduxState, IStore } from '../../../app/types';
  5. import {
  6. IconBell,
  7. IconCalendar,
  8. IconGear,
  9. IconImage,
  10. IconModerator,
  11. IconShortcuts,
  12. IconUser,
  13. IconVideo,
  14. IconVolumeUp
  15. } from '../../../base/icons/svg';
  16. import DialogWithTabs, { IDialogTab } from '../../../base/ui/components/web/DialogWithTabs';
  17. import { isCalendarEnabled } from '../../../calendar-sync/functions.web';
  18. import { submitAudioDeviceSelectionTab, submitVideoDeviceSelectionTab } from '../../../device-selection/actions.web';
  19. import AudioDevicesSelection from '../../../device-selection/components/AudioDevicesSelection';
  20. import VideoDeviceSelection from '../../../device-selection/components/VideoDeviceSelection';
  21. import {
  22. getAudioDeviceSelectionDialogProps,
  23. getVideoDeviceSelectionDialogProps
  24. } from '../../../device-selection/functions.web';
  25. import { checkBlurSupport } from '../../../virtual-background/functions';
  26. import { iAmVisitor } from '../../../visitors/functions';
  27. import {
  28. submitModeratorTab,
  29. submitMoreTab,
  30. submitNotificationsTab,
  31. submitProfileTab,
  32. submitShortcutsTab,
  33. submitVirtualBackgroundTab
  34. } from '../../actions';
  35. import { SETTINGS_TABS } from '../../constants';
  36. import {
  37. getModeratorTabProps,
  38. getMoreTabProps,
  39. getNotificationsMap,
  40. getNotificationsTabProps,
  41. getProfileTabProps,
  42. getShortcutsTabProps,
  43. getVirtualBackgroundTabProps
  44. } from '../../functions';
  45. import CalendarTab from './CalendarTab';
  46. import ModeratorTab from './ModeratorTab';
  47. import MoreTab from './MoreTab';
  48. import NotificationsTab from './NotificationsTab';
  49. import ProfileTab from './ProfileTab';
  50. import ShortcutsTab from './ShortcutsTab';
  51. import VirtualBackgroundTab from './VirtualBackgroundTab';
  52. /**
  53. * The type of the React {@code Component} props of
  54. * {@link ConnectedSettingsDialog}.
  55. */
  56. interface IProps {
  57. /**
  58. * Information about the tabs to be rendered.
  59. */
  60. _tabs: IDialogTab<any>[];
  61. /**
  62. * Which settings tab should be initially displayed. If not defined then
  63. * the first tab will be displayed.
  64. */
  65. defaultTab: string;
  66. /**
  67. * Invoked to save changed settings.
  68. */
  69. dispatch: IStore['dispatch'];
  70. /**
  71. * Indicates whether the device selection dialog is displayed on the
  72. * welcome page or not.
  73. */
  74. isDisplayedOnWelcomePage: boolean;
  75. }
  76. const useStyles = makeStyles()(() => {
  77. return {
  78. settingsDialog: {
  79. display: 'flex',
  80. width: '100%'
  81. }
  82. };
  83. });
  84. const SettingsDialog = ({ _tabs, defaultTab, dispatch }: IProps) => {
  85. const { classes } = useStyles();
  86. const correctDefaultTab = _tabs.find(tab => tab.name === defaultTab)?.name;
  87. const tabs = _tabs.map(tab => {
  88. return {
  89. ...tab,
  90. className: `settings-pane ${classes.settingsDialog}`,
  91. submit: (...args: any) => tab.submit
  92. && dispatch(tab.submit(...args))
  93. };
  94. });
  95. return (
  96. <DialogWithTabs
  97. className = 'settings-dialog'
  98. defaultTab = { correctDefaultTab }
  99. tabs = { tabs }
  100. titleKey = 'settings.title' />
  101. );
  102. };
  103. /**
  104. * Maps (parts of) the Redux state to the associated props for the
  105. * {@code ConnectedSettingsDialog} component.
  106. *
  107. * @param {Object} state - The Redux state.
  108. * @param {Object} ownProps - The props passed to the component.
  109. * @private
  110. * @returns {{
  111. * tabs: Array<Object>
  112. * }}
  113. */
  114. function _mapStateToProps(state: IReduxState, ownProps: any) {
  115. const { isDisplayedOnWelcomePage } = ownProps;
  116. const configuredTabs = interfaceConfig.SETTINGS_SECTIONS || [];
  117. // The settings sections to display.
  118. const showDeviceSettings = configuredTabs.includes('devices');
  119. const moreTabProps = getMoreTabProps(state);
  120. const moderatorTabProps = getModeratorTabProps(state);
  121. const { showModeratorSettings } = moderatorTabProps;
  122. const showMoreTab = configuredTabs.includes('more');
  123. const showProfileSettings
  124. = configuredTabs.includes('profile') && !state['features/base/config'].disableProfile;
  125. const showCalendarSettings
  126. = configuredTabs.includes('calendar') && isCalendarEnabled(state);
  127. const showSoundsSettings = configuredTabs.includes('sounds');
  128. const enabledNotifications = getNotificationsMap(state);
  129. const showNotificationsSettings = Object.keys(enabledNotifications).length > 0;
  130. const virtualBackgroundSupported = checkBlurSupport();
  131. const tabs: IDialogTab<any>[] = [];
  132. const _iAmVisitor = iAmVisitor(state);
  133. if (showDeviceSettings) {
  134. tabs.push({
  135. name: SETTINGS_TABS.AUDIO,
  136. component: AudioDevicesSelection,
  137. labelKey: 'settings.audio',
  138. props: getAudioDeviceSelectionDialogProps(state, isDisplayedOnWelcomePage),
  139. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getAudioDeviceSelectionDialogProps>) => {
  140. // Ensure the device selection tab gets updated when new devices
  141. // are found by taking the new props and only preserving the
  142. // current user selected devices. If this were not done, the
  143. // tab would keep using a copy of the initial props it received,
  144. // leaving the device list to become stale.
  145. return {
  146. ...newProps,
  147. noiseSuppressionEnabled: tabState.noiseSuppressionEnabled,
  148. selectedAudioInputId: tabState.selectedAudioInputId,
  149. selectedAudioOutputId: tabState.selectedAudioOutputId
  150. };
  151. },
  152. submit: (newState: any) => submitAudioDeviceSelectionTab(newState, isDisplayedOnWelcomePage),
  153. icon: IconVolumeUp
  154. });
  155. !_iAmVisitor && tabs.push({
  156. name: SETTINGS_TABS.VIDEO,
  157. component: VideoDeviceSelection,
  158. labelKey: 'settings.video',
  159. props: getVideoDeviceSelectionDialogProps(state, isDisplayedOnWelcomePage),
  160. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getVideoDeviceSelectionDialogProps>) => {
  161. // Ensure the device selection tab gets updated when new devices
  162. // are found by taking the new props and only preserving the
  163. // current user selected devices. If this were not done, the
  164. // tab would keep using a copy of the initial props it received,
  165. // leaving the device list to become stale.
  166. return {
  167. ...newProps,
  168. currentFramerate: tabState?.currentFramerate,
  169. localFlipX: tabState.localFlipX,
  170. selectedVideoInputId: tabState.selectedVideoInputId
  171. };
  172. },
  173. submit: (newState: any) => submitVideoDeviceSelectionTab(newState, isDisplayedOnWelcomePage),
  174. icon: IconVideo
  175. });
  176. }
  177. if (virtualBackgroundSupported && !_iAmVisitor) {
  178. tabs.push({
  179. name: SETTINGS_TABS.VIRTUAL_BACKGROUND,
  180. component: VirtualBackgroundTab,
  181. labelKey: 'virtualBackground.title',
  182. props: getVirtualBackgroundTabProps(state, isDisplayedOnWelcomePage),
  183. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getVirtualBackgroundTabProps>,
  184. tabStates: any) => {
  185. const videoTabState = tabStates[tabs.findIndex(tab => tab.name === SETTINGS_TABS.VIDEO)];
  186. return {
  187. ...newProps,
  188. selectedVideoInputId: videoTabState?.selectedVideoInputId || newProps.selectedVideoInputId,
  189. options: tabState.options
  190. };
  191. },
  192. submit: (newState: any) => submitVirtualBackgroundTab(newState),
  193. cancel: () => {
  194. const { options } = getVirtualBackgroundTabProps(state, isDisplayedOnWelcomePage);
  195. return submitVirtualBackgroundTab({
  196. options: {
  197. backgroundType: options.backgroundType,
  198. enabled: options.backgroundEffectEnabled,
  199. url: options.virtualSource,
  200. selectedThumbnail: options.selectedThumbnail,
  201. blurValue: options.blurValue
  202. }
  203. }, true);
  204. },
  205. icon: IconImage
  206. });
  207. }
  208. if ((showSoundsSettings || showNotificationsSettings) && !_iAmVisitor) {
  209. tabs.push({
  210. name: SETTINGS_TABS.NOTIFICATIONS,
  211. component: NotificationsTab,
  212. labelKey: 'settings.notifications',
  213. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getNotificationsTabProps>) => {
  214. return {
  215. ...newProps,
  216. enabledNotifications: tabState?.enabledNotifications || {},
  217. soundsIncomingMessage: tabState?.soundsIncomingMessage,
  218. soundsParticipantJoined: tabState?.soundsParticipantJoined,
  219. soundsParticipantKnocking: tabState?.soundsParticipantKnocking,
  220. soundsParticipantLeft: tabState?.soundsParticipantLeft,
  221. soundsReactions: tabState?.soundsReactions,
  222. soundsTalkWhileMuted: tabState?.soundsTalkWhileMuted
  223. };
  224. },
  225. props: getNotificationsTabProps(state, showSoundsSettings),
  226. submit: submitNotificationsTab,
  227. icon: IconBell
  228. });
  229. }
  230. if (showModeratorSettings && !_iAmVisitor) {
  231. tabs.push({
  232. name: SETTINGS_TABS.MODERATOR,
  233. component: ModeratorTab,
  234. labelKey: 'settings.moderator',
  235. props: moderatorTabProps,
  236. propsUpdateFunction: (tabState: any, newProps: typeof moderatorTabProps) => {
  237. // Updates tab props, keeping users selection
  238. return {
  239. ...newProps,
  240. followMeEnabled: tabState?.followMeEnabled,
  241. startAudioMuted: tabState?.startAudioMuted,
  242. startVideoMuted: tabState?.startVideoMuted,
  243. startReactionsMuted: tabState?.startReactionsMuted
  244. };
  245. },
  246. submit: submitModeratorTab,
  247. icon: IconModerator
  248. });
  249. }
  250. if (showProfileSettings) {
  251. tabs.push({
  252. name: SETTINGS_TABS.PROFILE,
  253. component: ProfileTab,
  254. labelKey: 'profile.title',
  255. props: getProfileTabProps(state),
  256. submit: submitProfileTab,
  257. icon: IconUser
  258. });
  259. }
  260. if (showCalendarSettings && !_iAmVisitor) {
  261. tabs.push({
  262. name: SETTINGS_TABS.CALENDAR,
  263. component: CalendarTab,
  264. labelKey: 'settings.calendar.title',
  265. icon: IconCalendar
  266. });
  267. }
  268. !_iAmVisitor && tabs.push({
  269. name: SETTINGS_TABS.SHORTCUTS,
  270. component: ShortcutsTab,
  271. labelKey: 'settings.shortcuts',
  272. props: getShortcutsTabProps(state, isDisplayedOnWelcomePage),
  273. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getShortcutsTabProps>) => {
  274. // Updates tab props, keeping users selection
  275. return {
  276. ...newProps,
  277. keyboardShortcutsEnabled: tabState?.keyboardShortcutsEnabled
  278. };
  279. },
  280. submit: submitShortcutsTab,
  281. icon: IconShortcuts
  282. });
  283. if (showMoreTab && !_iAmVisitor) {
  284. tabs.push({
  285. name: SETTINGS_TABS.MORE,
  286. component: MoreTab,
  287. labelKey: 'settings.more',
  288. props: moreTabProps,
  289. propsUpdateFunction: (tabState: any, newProps: typeof moreTabProps) => {
  290. // Updates tab props, keeping users selection
  291. return {
  292. ...newProps,
  293. currentLanguage: tabState?.currentLanguage,
  294. hideSelfView: tabState?.hideSelfView,
  295. showPrejoinPage: tabState?.showPrejoinPage,
  296. maxStageParticipants: tabState?.maxStageParticipants
  297. };
  298. },
  299. submit: submitMoreTab,
  300. icon: IconGear
  301. });
  302. }
  303. return { _tabs: tabs };
  304. }
  305. export default connect(_mapStateToProps)(SettingsDialog);