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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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, checkVirtualBackgroundEnabled } 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 enableVirtualBackground = checkVirtualBackgroundEnabled(state);
  132. const tabs: IDialogTab<any>[] = [];
  133. const _iAmVisitor = iAmVisitor(state);
  134. if (showDeviceSettings) {
  135. tabs.push({
  136. name: SETTINGS_TABS.AUDIO,
  137. component: AudioDevicesSelection,
  138. labelKey: 'settings.audio',
  139. props: getAudioDeviceSelectionDialogProps(state, isDisplayedOnWelcomePage),
  140. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getAudioDeviceSelectionDialogProps>) => {
  141. // Ensure the device selection tab gets updated when new devices
  142. // are found by taking the new props and only preserving the
  143. // current user selected devices. If this were not done, the
  144. // tab would keep using a copy of the initial props it received,
  145. // leaving the device list to become stale.
  146. return {
  147. ...newProps,
  148. noiseSuppressionEnabled: tabState.noiseSuppressionEnabled,
  149. selectedAudioInputId: tabState.selectedAudioInputId,
  150. selectedAudioOutputId: tabState.selectedAudioOutputId
  151. };
  152. },
  153. submit: (newState: any) => submitAudioDeviceSelectionTab(newState, isDisplayedOnWelcomePage),
  154. icon: IconVolumeUp
  155. });
  156. !_iAmVisitor && tabs.push({
  157. name: SETTINGS_TABS.VIDEO,
  158. component: VideoDeviceSelection,
  159. labelKey: 'settings.video',
  160. props: getVideoDeviceSelectionDialogProps(state, isDisplayedOnWelcomePage),
  161. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getVideoDeviceSelectionDialogProps>) => {
  162. // Ensure the device selection tab gets updated when new devices
  163. // are found by taking the new props and only preserving the
  164. // current user selected devices. If this were not done, the
  165. // tab would keep using a copy of the initial props it received,
  166. // leaving the device list to become stale.
  167. return {
  168. ...newProps,
  169. currentFramerate: tabState?.currentFramerate,
  170. localFlipX: tabState.localFlipX,
  171. selectedVideoInputId: tabState.selectedVideoInputId
  172. };
  173. },
  174. submit: (newState: any) => submitVideoDeviceSelectionTab(newState, isDisplayedOnWelcomePage),
  175. icon: IconVideo
  176. });
  177. }
  178. if (virtualBackgroundSupported && !_iAmVisitor && enableVirtualBackground) {
  179. tabs.push({
  180. name: SETTINGS_TABS.VIRTUAL_BACKGROUND,
  181. component: VirtualBackgroundTab,
  182. labelKey: 'virtualBackground.title',
  183. props: getVirtualBackgroundTabProps(state, isDisplayedOnWelcomePage),
  184. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getVirtualBackgroundTabProps>,
  185. tabStates: any) => {
  186. const videoTabState = tabStates[tabs.findIndex(tab => tab.name === SETTINGS_TABS.VIDEO)];
  187. return {
  188. ...newProps,
  189. selectedVideoInputId: videoTabState?.selectedVideoInputId || newProps.selectedVideoInputId,
  190. options: tabState.options
  191. };
  192. },
  193. submit: (newState: any) => submitVirtualBackgroundTab(newState),
  194. cancel: () => {
  195. const { options } = getVirtualBackgroundTabProps(state, isDisplayedOnWelcomePage);
  196. return submitVirtualBackgroundTab({ options }, true);
  197. },
  198. icon: IconImage
  199. });
  200. }
  201. if ((showSoundsSettings || showNotificationsSettings) && !_iAmVisitor) {
  202. tabs.push({
  203. name: SETTINGS_TABS.NOTIFICATIONS,
  204. component: NotificationsTab,
  205. labelKey: 'settings.notifications',
  206. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getNotificationsTabProps>) => {
  207. return {
  208. ...newProps,
  209. enabledNotifications: tabState?.enabledNotifications || {},
  210. soundsIncomingMessage: tabState?.soundsIncomingMessage,
  211. soundsParticipantJoined: tabState?.soundsParticipantJoined,
  212. soundsParticipantKnocking: tabState?.soundsParticipantKnocking,
  213. soundsParticipantLeft: tabState?.soundsParticipantLeft,
  214. soundsReactions: tabState?.soundsReactions,
  215. soundsTalkWhileMuted: tabState?.soundsTalkWhileMuted
  216. };
  217. },
  218. props: getNotificationsTabProps(state, showSoundsSettings),
  219. submit: submitNotificationsTab,
  220. icon: IconBell
  221. });
  222. }
  223. if (showModeratorSettings && !_iAmVisitor) {
  224. tabs.push({
  225. name: SETTINGS_TABS.MODERATOR,
  226. component: ModeratorTab,
  227. labelKey: 'settings.moderator',
  228. props: moderatorTabProps,
  229. propsUpdateFunction: (tabState: any, newProps: typeof moderatorTabProps) => {
  230. // Updates tab props, keeping users selection
  231. return {
  232. ...newProps,
  233. followMeEnabled: tabState?.followMeEnabled,
  234. followMeRecorderEnabled: tabState?.followMeRecorderEnabled,
  235. startAudioMuted: tabState?.startAudioMuted,
  236. startVideoMuted: tabState?.startVideoMuted,
  237. startReactionsMuted: tabState?.startReactionsMuted
  238. };
  239. },
  240. submit: submitModeratorTab,
  241. icon: IconModerator
  242. });
  243. }
  244. if (showProfileSettings) {
  245. tabs.push({
  246. name: SETTINGS_TABS.PROFILE,
  247. component: ProfileTab,
  248. labelKey: 'profile.title',
  249. props: getProfileTabProps(state),
  250. submit: submitProfileTab,
  251. icon: IconUser
  252. });
  253. }
  254. if (showCalendarSettings && !_iAmVisitor) {
  255. tabs.push({
  256. name: SETTINGS_TABS.CALENDAR,
  257. component: CalendarTab,
  258. labelKey: 'settings.calendar.title',
  259. icon: IconCalendar
  260. });
  261. }
  262. !_iAmVisitor && tabs.push({
  263. name: SETTINGS_TABS.SHORTCUTS,
  264. component: ShortcutsTab,
  265. labelKey: 'settings.shortcuts',
  266. props: getShortcutsTabProps(state, isDisplayedOnWelcomePage),
  267. propsUpdateFunction: (tabState: any, newProps: ReturnType<typeof getShortcutsTabProps>) => {
  268. // Updates tab props, keeping users selection
  269. return {
  270. ...newProps,
  271. keyboardShortcutsEnabled: tabState?.keyboardShortcutsEnabled
  272. };
  273. },
  274. submit: submitShortcutsTab,
  275. icon: IconShortcuts
  276. });
  277. if (showMoreTab && !_iAmVisitor) {
  278. tabs.push({
  279. name: SETTINGS_TABS.MORE,
  280. component: MoreTab,
  281. labelKey: 'settings.more',
  282. props: moreTabProps,
  283. propsUpdateFunction: (tabState: any, newProps: typeof moreTabProps) => {
  284. // Updates tab props, keeping users selection
  285. return {
  286. ...newProps,
  287. currentLanguage: tabState?.currentLanguage,
  288. hideSelfView: tabState?.hideSelfView,
  289. showPrejoinPage: tabState?.showPrejoinPage,
  290. maxStageParticipants: tabState?.maxStageParticipants
  291. };
  292. },
  293. submit: submitMoreTab,
  294. icon: IconGear
  295. });
  296. }
  297. return { _tabs: tabs };
  298. }
  299. export default connect(_mapStateToProps)(SettingsDialog);