mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-05-11 06:32:30 +00:00
The user can set the max number of participants that can be displayed on stage Send the number on follow me to all participants
380 lines
11 KiB
JavaScript
380 lines
11 KiB
JavaScript
// @flow
|
|
|
|
import { withStyles } from '@material-ui/styles';
|
|
import React, { Component } from 'react';
|
|
|
|
import { getAvailableDevices } from '../../../base/devices';
|
|
import { DialogWithTabs, hideDialog } from '../../../base/dialog';
|
|
import { connect } from '../../../base/redux';
|
|
import { isCalendarEnabled } from '../../../calendar-sync';
|
|
import {
|
|
DeviceSelection,
|
|
getDeviceSelectionDialogProps,
|
|
submitDeviceSelectionTab
|
|
} from '../../../device-selection';
|
|
import {
|
|
submitModeratorTab,
|
|
submitMoreTab,
|
|
submitProfileTab,
|
|
submitSoundsTab
|
|
} from '../../actions';
|
|
import { SETTINGS_TABS } from '../../constants';
|
|
import {
|
|
getModeratorTabProps,
|
|
getMoreTabProps,
|
|
getProfileTabProps,
|
|
getSoundsTabProps
|
|
} from '../../functions';
|
|
|
|
import CalendarTab from './CalendarTab';
|
|
import ModeratorTab from './ModeratorTab';
|
|
import MoreTab from './MoreTab';
|
|
import ProfileTab from './ProfileTab';
|
|
import SoundsTab from './SoundsTab';
|
|
|
|
declare var interfaceConfig: Object;
|
|
|
|
/**
|
|
* The type of the React {@code Component} props of
|
|
* {@link ConnectedSettingsDialog}.
|
|
*/
|
|
type Props = {
|
|
|
|
/**
|
|
* An object containing the CSS classes.
|
|
*/
|
|
classes: Object,
|
|
|
|
/**
|
|
* Which settings tab should be initially displayed. If not defined then
|
|
* the first tab will be displayed.
|
|
*/
|
|
defaultTab: string,
|
|
|
|
/**
|
|
* Information about the tabs to be rendered.
|
|
*/
|
|
_tabs: Array<Object>,
|
|
|
|
/**
|
|
* Invoked to save changed settings.
|
|
*/
|
|
dispatch: Function
|
|
};
|
|
|
|
/**
|
|
* Creates the styles for the component.
|
|
*
|
|
* @param {Object} theme - The current UI theme.
|
|
*
|
|
* @returns {Object}
|
|
*/
|
|
const styles = theme => {
|
|
return {
|
|
settingsDialog: {
|
|
display: 'flex',
|
|
width: '100%',
|
|
|
|
'&.profile-pane': {
|
|
flexDirection: 'column'
|
|
},
|
|
|
|
'& .auth-name': {
|
|
marginBottom: `${theme.spacing(1)}px`
|
|
},
|
|
|
|
'& .calendar-tab, & .device-selection': {
|
|
marginTop: '20px'
|
|
},
|
|
|
|
'& .mock-atlaskit-label': {
|
|
color: '#b8c7e0',
|
|
fontSize: '12px',
|
|
fontWeight: 600,
|
|
lineHeight: 1.33,
|
|
padding: `20px 0px ${theme.spacing(1)}px 0px`
|
|
},
|
|
|
|
'& input[type="checkbox"]:checked + svg': {
|
|
'--checkbox-background-color': '#6492e7',
|
|
'--checkbox-border-color': '#6492e7'
|
|
},
|
|
|
|
'& input[type="checkbox"] + svg + span': {
|
|
color: '#9FB0CC'
|
|
},
|
|
|
|
[[ '& .calendar-tab',
|
|
'& .more-tab',
|
|
'& .box' ]]: {
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
width: '100%'
|
|
},
|
|
|
|
'& .profile-edit': {
|
|
display: 'flex',
|
|
width: '100%'
|
|
},
|
|
|
|
'& .profile-edit-field': {
|
|
flex: 0.5,
|
|
marginRight: '20px'
|
|
},
|
|
|
|
'& .settings-sub-pane': {
|
|
flex: 1
|
|
},
|
|
|
|
'& .settings-sub-pane .right': {
|
|
flex: 1
|
|
},
|
|
'& .settings-sub-pane .left': {
|
|
flex: 1
|
|
},
|
|
|
|
'& .settings-sub-pane-element': {
|
|
textAlign: 'left',
|
|
flex: 1
|
|
},
|
|
|
|
'& .moderator-settings-wrapper': {
|
|
paddingTop: '20px'
|
|
},
|
|
|
|
'& .calendar-tab': {
|
|
alignItems: 'center',
|
|
flexDirection: 'column',
|
|
fontSize: '14px',
|
|
minHeight: '100px',
|
|
textAlign: 'center'
|
|
},
|
|
|
|
'& .calendar-tab-sign-in': {
|
|
marginTop: '20px'
|
|
},
|
|
|
|
'& .sign-out-cta': {
|
|
marginBottom: '20px'
|
|
},
|
|
|
|
'@media only screen and (max-width: 700px)': {
|
|
'& .device-selection': {
|
|
display: 'flex',
|
|
flexDirection: 'column'
|
|
},
|
|
|
|
'& .more-tab': {
|
|
flexDirection: 'column'
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* A React {@code Component} for displaying a dialog to modify local settings
|
|
* and conference-wide (moderator) settings. This version is connected to
|
|
* redux to get the current settings.
|
|
*
|
|
* @augments Component
|
|
*/
|
|
class SettingsDialog extends Component<Props> {
|
|
/**
|
|
* Initializes a new {@code ConnectedSettingsDialog} instance.
|
|
*
|
|
* @param {Props} props - The React {@code Component} props to initialize
|
|
* the new {@code ConnectedSettingsDialog} instance with.
|
|
*/
|
|
constructor(props: Props) {
|
|
super(props);
|
|
|
|
// Bind event handlers so they are only bound once for every instance.
|
|
this._closeDialog = this._closeDialog.bind(this);
|
|
}
|
|
|
|
/**
|
|
* Implements React's {@link Component#render()}.
|
|
*
|
|
* @inheritdoc
|
|
* @returns {ReactElement}
|
|
*/
|
|
render() {
|
|
const { _tabs, defaultTab, dispatch } = this.props;
|
|
const onSubmit = this._closeDialog;
|
|
const defaultTabIdx
|
|
= _tabs.findIndex(({ name }) => name === defaultTab);
|
|
const tabs = _tabs.map(tab => {
|
|
return {
|
|
...tab,
|
|
onMount: tab.onMount
|
|
? (...args) => dispatch(tab.onMount(...args))
|
|
: undefined,
|
|
submit: (...args) => tab.submit
|
|
&& dispatch(tab.submit(...args))
|
|
};
|
|
});
|
|
|
|
return (
|
|
<DialogWithTabs
|
|
closeDialog = { this._closeDialog }
|
|
cssClassName = 'settings-dialog'
|
|
defaultTab = {
|
|
defaultTabIdx === -1 ? undefined : defaultTabIdx
|
|
}
|
|
onSubmit = { onSubmit }
|
|
tabs = { tabs }
|
|
titleKey = 'settings.title' />
|
|
);
|
|
}
|
|
|
|
_closeDialog: () => void;
|
|
|
|
/**
|
|
* Callback invoked to close the dialog without saving changes.
|
|
*
|
|
* @private
|
|
* @returns {void}
|
|
*/
|
|
_closeDialog() {
|
|
this.props.dispatch(hideDialog());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maps (parts of) the Redux state to the associated props for the
|
|
* {@code ConnectedSettingsDialog} component.
|
|
*
|
|
* @param {Object} state - The Redux state.
|
|
* @param {Object} ownProps - The props passed to the component.
|
|
* @private
|
|
* @returns {{
|
|
* tabs: Array<Object>
|
|
* }}
|
|
*/
|
|
function _mapStateToProps(state, ownProps) {
|
|
const { classes } = ownProps;
|
|
const configuredTabs = interfaceConfig.SETTINGS_SECTIONS || [];
|
|
|
|
// The settings sections to display.
|
|
const showDeviceSettings = configuredTabs.includes('devices');
|
|
const moreTabProps = getMoreTabProps(state);
|
|
const moderatorTabProps = getModeratorTabProps(state);
|
|
const { showModeratorSettings } = moderatorTabProps;
|
|
const { showLanguageSettings, showNotificationsSettings, showPrejoinSettings } = moreTabProps;
|
|
const showMoreTab = showLanguageSettings || showNotificationsSettings || showPrejoinSettings;
|
|
const showProfileSettings
|
|
= configuredTabs.includes('profile') && !state['features/base/config'].disableProfile;
|
|
const showCalendarSettings
|
|
= configuredTabs.includes('calendar') && isCalendarEnabled(state);
|
|
const showSoundsSettings = configuredTabs.includes('sounds');
|
|
const tabs = [];
|
|
|
|
if (showDeviceSettings) {
|
|
tabs.push({
|
|
name: SETTINGS_TABS.DEVICES,
|
|
component: DeviceSelection,
|
|
label: 'settings.devices',
|
|
onMount: getAvailableDevices,
|
|
props: getDeviceSelectionDialogProps(state),
|
|
propsUpdateFunction: (tabState, newProps) => {
|
|
// Ensure the device selection tab gets updated when new devices
|
|
// are found by taking the new props and only preserving the
|
|
// current user selected devices. If this were not done, the
|
|
// tab would keep using a copy of the initial props it received,
|
|
// leaving the device list to become stale.
|
|
|
|
return {
|
|
...newProps,
|
|
selectedAudioInputId: tabState.selectedAudioInputId,
|
|
selectedAudioOutputId: tabState.selectedAudioOutputId,
|
|
selectedVideoInputId: tabState.selectedVideoInputId
|
|
};
|
|
},
|
|
styles: `settings-pane ${classes.settingsDialog} devices-pane`,
|
|
submit: submitDeviceSelectionTab
|
|
});
|
|
}
|
|
|
|
if (showProfileSettings) {
|
|
tabs.push({
|
|
name: SETTINGS_TABS.PROFILE,
|
|
component: ProfileTab,
|
|
label: 'profile.title',
|
|
props: getProfileTabProps(state),
|
|
styles: `settings-pane ${classes.settingsDialog} profile-pane`,
|
|
submit: submitProfileTab
|
|
});
|
|
}
|
|
|
|
if (showModeratorSettings) {
|
|
tabs.push({
|
|
name: SETTINGS_TABS.MODERATOR,
|
|
component: ModeratorTab,
|
|
label: 'settings.moderator',
|
|
props: moderatorTabProps,
|
|
propsUpdateFunction: (tabState, newProps) => {
|
|
// Updates tab props, keeping users selection
|
|
|
|
return {
|
|
...newProps,
|
|
followMeEnabled: tabState?.followMeEnabled,
|
|
startAudioMuted: tabState?.startAudioMuted,
|
|
startVideoMuted: tabState?.startVideoMuted,
|
|
startReactionsMuted: tabState?.startReactionsMuted
|
|
};
|
|
},
|
|
styles: `settings-pane ${classes.settingsDialog} moderator-pane`,
|
|
submit: submitModeratorTab
|
|
});
|
|
}
|
|
|
|
if (showCalendarSettings) {
|
|
tabs.push({
|
|
name: SETTINGS_TABS.CALENDAR,
|
|
component: CalendarTab,
|
|
label: 'settings.calendar.title',
|
|
styles: `settings-pane ${classes.settingsDialog} calendar-pane`
|
|
});
|
|
}
|
|
|
|
if (showSoundsSettings) {
|
|
tabs.push({
|
|
name: SETTINGS_TABS.SOUNDS,
|
|
component: SoundsTab,
|
|
label: 'settings.sounds',
|
|
props: getSoundsTabProps(state),
|
|
styles: `settings-pane ${classes.settingsDialog} profile-pane`,
|
|
submit: submitSoundsTab
|
|
});
|
|
}
|
|
|
|
if (showMoreTab) {
|
|
tabs.push({
|
|
name: SETTINGS_TABS.MORE,
|
|
component: MoreTab,
|
|
label: 'settings.more',
|
|
props: moreTabProps,
|
|
propsUpdateFunction: (tabState, newProps) => {
|
|
// Updates tab props, keeping users selection
|
|
|
|
return {
|
|
...newProps,
|
|
currentFramerate: tabState?.currentFramerate,
|
|
currentLanguage: tabState?.currentLanguage,
|
|
hideSelfView: tabState?.hideSelfView,
|
|
showPrejoinPage: tabState?.showPrejoinPage,
|
|
enabledNotifications: tabState?.enabledNotifications,
|
|
maxStageParticipants: tabState?.maxStageParticipants
|
|
};
|
|
},
|
|
styles: `settings-pane ${classes.settingsDialog} more-pane`,
|
|
submit: submitMoreTab
|
|
});
|
|
}
|
|
|
|
return { _tabs: tabs };
|
|
}
|
|
|
|
export default withStyles(styles)(connect(_mapStateToProps)(SettingsDialog));
|