mirror of https://github.com/jitsi/jitsi-meet
The goal is to reduce usage on atlassian/aui. New components have been created to display the settings panel. Language selection will reach into i18n for state whereas moderator options will keep state in redux.pull/2250/head
parent
0eafee2a95
commit
c9b54845d9
@ -0,0 +1,87 @@ |
||||
import Button from '@atlaskit/button'; |
||||
import PropTypes from 'prop-types'; |
||||
import React, { Component } from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
|
||||
import { translate } from '../../base/i18n'; |
||||
import { openDeviceSelectionDialog } from '../../device-selection'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button for opening the |
||||
* {@code DeviceSelectionDialog}. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class DeviceSelectionButton extends Component { |
||||
/** |
||||
* {@code DeviceSelectionButton} component's property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* Invoked to display the {@code DeviceSelectionDialog}. |
||||
*/ |
||||
dispatch: PropTypes.func, |
||||
|
||||
/** |
||||
* Whether or not the button's title should be displayed. |
||||
*/ |
||||
showTitle: PropTypes.bool, |
||||
|
||||
/** |
||||
* Invoked to obtain translated strings. |
||||
*/ |
||||
t: PropTypes.func |
||||
}; |
||||
|
||||
/** |
||||
* Initializes a new {@code DeviceSelectionButton} instance. |
||||
* |
||||
* @param {Object} props - The read-only properties with which the new |
||||
* instance is to be initialized. |
||||
*/ |
||||
constructor(props) { |
||||
super(props); |
||||
|
||||
// Bind event handler so it is only bound once for every instance.
|
||||
this._onOpenDeviceSelectionDialog |
||||
= this._onOpenDeviceSelectionDialog.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<div> |
||||
{ this.props.showTitle |
||||
? <div className = 'subTitle'> |
||||
{ this.props.t('settings.audioVideo') } |
||||
</div> |
||||
: null } |
||||
<Button |
||||
appearance = 'primary' |
||||
onClick = { this._onOpenDeviceSelectionDialog } |
||||
shouldFitContainer = { true }> |
||||
{ this.props.t('deviceSelection.deviceSettings') } |
||||
</Button> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Opens the {@code DeviceSelectionDialog}. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onOpenDeviceSelectionDialog() { |
||||
this.props.dispatch(openDeviceSelectionDialog()); |
||||
} |
||||
} |
||||
|
||||
export default translate(connect()(DeviceSelectionButton)); |
@ -0,0 +1,179 @@ |
||||
import DropdownMenu, { |
||||
DropdownItem, |
||||
DropdownItemGroup |
||||
} from '@atlaskit/dropdown-menu'; |
||||
import PropTypes from 'prop-types'; |
||||
import React, { Component } from 'react'; |
||||
|
||||
import { DEFAULT_LANGUAGE, LANGUAGES, translate } from '../../base/i18n'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a dropdown for changing |
||||
* application text to another language. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class LanguageSelectDropdown extends Component { |
||||
/** |
||||
* {@code LanguageSelectDropdown} component's property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* The translation service. |
||||
*/ |
||||
i18n: PropTypes.object, |
||||
|
||||
/** |
||||
* Invoked to obtain translated strings. |
||||
*/ |
||||
t: PropTypes.func |
||||
}; |
||||
|
||||
|
||||
/** |
||||
* {@code LanguageSelectDropdown} component's local state. |
||||
* |
||||
* @type {Object} |
||||
* @property {string|null} currentLanguage - The currently selected language |
||||
* the application should be displayed in. |
||||
* @property {boolean} isLanguageSelectOpen - Whether or not the dropdown |
||||
* should be displayed as open. |
||||
*/ |
||||
state = { |
||||
currentLanguage: null, |
||||
isLanguageSelectOpen: false |
||||
}; |
||||
|
||||
/** |
||||
* Initializes a new {@code LanguageSelectDropdown} instance. |
||||
* |
||||
* @param {Object} props - The read-only properties with which the new |
||||
* instance is to be initialized. |
||||
*/ |
||||
constructor(props) { |
||||
super(props); |
||||
|
||||
this.state.currentLanguage |
||||
= this.props.i18n.language || DEFAULT_LANGUAGE; |
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onLanguageSelected = this._onLanguageSelected.bind(this); |
||||
this._onSetDropdownOpen = this._onSetDropdownOpen.bind(this); |
||||
this._setCurrentLanguage = this._setCurrentLanguage.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Sets a listener to update the currently selected language if it is |
||||
* changed from somewhere else. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {void} |
||||
*/ |
||||
componentDidMount() { |
||||
this.props.i18n.on('languageChanged', this._setCurrentLanguage); |
||||
} |
||||
|
||||
/** |
||||
* Removes all listeners. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {void} |
||||
*/ |
||||
componentWillUnmount() { |
||||
this.props.i18n.off('languageChanged', this._setCurrentLanguage); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { t } = this.props; |
||||
const { currentLanguage } = this.state; |
||||
|
||||
const languageItems = LANGUAGES.map(language => |
||||
// eslint-disable-next-line react/jsx-wrap-multilines
|
||||
<DropdownItem |
||||
key = { language } |
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick = { () => this._onLanguageSelected(language) }> |
||||
{ t(`languages:${language}`) } |
||||
</DropdownItem> |
||||
); |
||||
|
||||
return ( |
||||
<div> |
||||
<DropdownMenu |
||||
isOpen = { this.state.isLanguageSelectOpen } |
||||
onOpenChange = { this._onSetDropdownOpen } |
||||
shouldFitContainer = { true } |
||||
trigger = { currentLanguage |
||||
? t(`languages:${currentLanguage}`) |
||||
: '' } |
||||
triggerButtonProps = {{ |
||||
appearance: 'primary', |
||||
shouldFitContainer: true |
||||
}} |
||||
triggerType = 'button'> |
||||
<DropdownItemGroup> |
||||
{ languageItems } |
||||
</DropdownItemGroup> |
||||
</DropdownMenu> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Updates the application's currently displayed language. |
||||
* |
||||
* @param {string} language - The language code for the language to display. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onLanguageSelected(language) { |
||||
const previousLanguage = this.state.currentLanguage; |
||||
|
||||
this.setState({ |
||||
currentLanguage: language, |
||||
isLanguageSelectOpen: false |
||||
}); |
||||
|
||||
this.props.i18n.changeLanguage(language, error => { |
||||
if (error) { |
||||
this._setCurrentLanguage(previousLanguage); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Set whether or not the dropdown should be open. |
||||
* |
||||
* @param {Object} dropdownEvent - The event returned from requesting the |
||||
* open state of the dropdown be changed. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onSetDropdownOpen(dropdownEvent) { |
||||
this.setState({ |
||||
isLanguageSelectOpen: dropdownEvent.isOpen |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Updates the known current language of the application. |
||||
* |
||||
* @param {string} currentLanguage - The language code for the current |
||||
* language. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_setCurrentLanguage(currentLanguage) { |
||||
this.setState({ currentLanguage }); |
||||
} |
||||
} |
||||
|
||||
export default translate(LanguageSelectDropdown); |
@ -0,0 +1,199 @@ |
||||
import PropTypes from 'prop-types'; |
||||
import React, { Component } from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
|
||||
import { setFollowMe, setStartMutedPolicy } from '../../base/conference'; |
||||
import { translate } from '../../base/i18n'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays checkboxes for enabling |
||||
* and disabling moderator-only conference features. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class ModeratorCheckboxes extends Component { |
||||
/** |
||||
* {@code ModeratorCheckboxes} component's property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* Whether or not the Follow Me feature is currently enabled. |
||||
*/ |
||||
_followMeEnabled: PropTypes.bool, |
||||
|
||||
/** |
||||
* Whether or not new members will join the conference as audio muted. |
||||
*/ |
||||
_startAudioMutedPolicy: PropTypes.bool, |
||||
|
||||
/** |
||||
* Whether or note new member will join the conference as video muted. |
||||
*/ |
||||
_startVideoMutedPolicy: PropTypes.bool, |
||||
|
||||
/** |
||||
* Invoked to enable and disable moderator-only conference features. |
||||
*/ |
||||
dispatch: PropTypes.func, |
||||
|
||||
/** |
||||
* Whether or not the title should be displayed. |
||||
*/ |
||||
showTitle: PropTypes.bool, |
||||
|
||||
/** |
||||
* Invokted to obtain translated strings. |
||||
*/ |
||||
t: PropTypes.func |
||||
}; |
||||
|
||||
/** |
||||
* Initializes a new {@code ModeratorCheckboxes} instance. |
||||
* |
||||
* @param {Object} props - The read-only properties with which the new |
||||
* instance is to be initialized. |
||||
*/ |
||||
constructor(props) { |
||||
super(props); |
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onSetFollowMeSetting |
||||
= this._onSetFollowMeSetting.bind(this); |
||||
this._onSetStartAudioMutedPolicy |
||||
= this._onSetStartAudioMutedPolicy.bind(this); |
||||
this._onSetStartVideoMutedPolicy |
||||
= this._onSetStartVideoMutedPolicy.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { |
||||
_followMeEnabled, |
||||
_startAudioMutedPolicy, |
||||
_startVideoMutedPolicy, |
||||
showTitle, |
||||
t |
||||
} = this.props; |
||||
|
||||
return ( |
||||
<div> |
||||
{ showTitle |
||||
? <div className = 'subTitle'> |
||||
{ t('settings.moderator') } |
||||
</div> |
||||
: null } |
||||
<div className = 'moderator-option'> |
||||
<input |
||||
checked = { _startAudioMutedPolicy } |
||||
className = 'moderator-checkbox' |
||||
id = 'startAudioMuted' |
||||
onChange = { this._onSetStartAudioMutedPolicy } |
||||
type = 'checkbox' /> |
||||
<label |
||||
className = 'moderator-checkbox-label' |
||||
htmlFor = 'startAudioMuted'> |
||||
{ t('settings.startAudioMuted') } |
||||
</label> |
||||
</div> |
||||
<div className = 'moderator-option'> |
||||
<input |
||||
checked = { _startVideoMutedPolicy } |
||||
className = 'moderator-checkbox' |
||||
id = 'startVideoMuted' |
||||
onChange = { this._onSetStartVideoMutedPolicy } |
||||
type = 'checkbox' /> |
||||
<label |
||||
className = 'moderator-checkbox-label' |
||||
htmlFor = 'startVideoMuted'> |
||||
{ t('settings.startVideoMuted') } |
||||
</label> |
||||
</div> |
||||
<div className = 'moderator-option'> |
||||
<input |
||||
checked = { _followMeEnabled } |
||||
className = 'moderator-checkbox' |
||||
id = 'followMeCheckBox' |
||||
onChange = { this._onSetFollowMeSetting } |
||||
type = 'checkbox' /> |
||||
<label |
||||
className = 'moderator-checkbox-label' |
||||
htmlFor = 'followMeCheckBox'> |
||||
{ t('settings.followMe') } |
||||
</label> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Toggles the Follow Me feature. |
||||
* |
||||
* @param {Object} event - The dom event returned from changes the checkbox. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onSetFollowMeSetting(event) { |
||||
this.props.dispatch(setFollowMe(event.target.checked)); |
||||
} |
||||
|
||||
/** |
||||
* Toggles whether or not new members should join the conference as audio |
||||
* muted. |
||||
* |
||||
* @param {Object} event - The dom event returned from changes the checkbox. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onSetStartAudioMutedPolicy(event) { |
||||
this.props.dispatch(setStartMutedPolicy( |
||||
event.target.checked, this.props._startVideoMutedPolicy)); |
||||
} |
||||
|
||||
/** |
||||
* Toggles whether or not new members should join the conference as video |
||||
* muted. |
||||
* |
||||
* @param {Object} event - The dom event returned from changes the checkbox. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onSetStartVideoMutedPolicy(event) { |
||||
this.props.dispatch(setStartMutedPolicy( |
||||
this.props._startAudioMutedPolicy, event.target.checked)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Maps (parts of) the Redux state to the associated props for the |
||||
* {@code ModeratorCheckboxes} component. |
||||
* |
||||
* @param {Object} state - The Redux state. |
||||
* @private |
||||
* @returns {{ |
||||
* _followMeEnabled: boolean, |
||||
* _startAudioMutedPolicy: boolean, |
||||
* _startVideoMutedPolicy: boolean |
||||
* }} |
||||
*/ |
||||
function _mapStateToProps(state) { |
||||
const { |
||||
followMeEnabled, |
||||
startAudioMutedPolicy, |
||||
startVideoMutedPolicy |
||||
} = state['features/base/conference']; |
||||
|
||||
return { |
||||
_followMeEnabled: Boolean(followMeEnabled), |
||||
_startAudioMutedPolicy: Boolean(startAudioMutedPolicy), |
||||
_startVideoMutedPolicy: Boolean(startVideoMutedPolicy) |
||||
}; |
||||
} |
||||
|
||||
export default translate(connect(_mapStateToProps)(ModeratorCheckboxes)); |
@ -0,0 +1,108 @@ |
||||
import PropTypes from 'prop-types'; |
||||
import React, { Component } from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
|
||||
import { translate } from '../../base/i18n'; |
||||
import { getLocalParticipant, PARTICIPANT_ROLE } from '../../base/participants'; |
||||
|
||||
import DeviceSelectionButton from './DeviceSelectionButton'; |
||||
import LanguageSelectDropdown from './LanguageSelectDropdown'; |
||||
import ModeratorCheckboxes from './ModeratorCheckboxes'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which various ways to change application |
||||
* settings. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class SettingsMenu extends Component { |
||||
/** |
||||
* {@code SettingsMenu} component's property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* Whether or not the local user is a moderator. |
||||
*/ |
||||
_isModerator: PropTypes.bool, |
||||
|
||||
/** |
||||
* Whether or not the button to open device selection should display. |
||||
*/ |
||||
showDeviceSettings: PropTypes.bool, |
||||
|
||||
/** |
||||
* Whether or not the dropdown to change the current translated language |
||||
* should display. |
||||
*/ |
||||
showLanguageSettings: PropTypes.bool, |
||||
|
||||
/** |
||||
* Whether or not moderator-only actions that affect the conference |
||||
* should display. |
||||
*/ |
||||
showModeratorSettings: PropTypes.bool, |
||||
|
||||
/** |
||||
* Whether or not menu section should have section titles displayed. |
||||
*/ |
||||
showTitles: PropTypes.bool, |
||||
|
||||
/** |
||||
* Invoked to obtain translated strings. |
||||
*/ |
||||
t: PropTypes.func |
||||
}; |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { |
||||
_isModerator, |
||||
showDeviceSettings, |
||||
showLanguageSettings, |
||||
showModeratorSettings, |
||||
showTitles, |
||||
t |
||||
} = this.props; |
||||
|
||||
return ( |
||||
<div className = 'settings-menu'> |
||||
<div className = 'title'> |
||||
{ t('settings.title') } |
||||
</div> |
||||
{ showLanguageSettings |
||||
? <LanguageSelectDropdown /> |
||||
: null } |
||||
{ showDeviceSettings |
||||
? <DeviceSelectionButton showTitle = { showTitles } /> |
||||
: null } |
||||
{ _isModerator && showModeratorSettings |
||||
? <ModeratorCheckboxes showTitle = { showTitles } /> |
||||
: null } |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Maps parts of Redux store to component prop types. |
||||
* |
||||
* @param {Object} state - Snapshot of Redux store. |
||||
* @returns {{ |
||||
* _isModerator: boolean |
||||
* }} |
||||
*/ |
||||
function _mapStateToProps(state) { |
||||
return { |
||||
_isModerator: |
||||
getLocalParticipant(state).role === PARTICIPANT_ROLE.MODERATOR |
||||
}; |
||||
} |
||||
|
||||
export default translate(connect(_mapStateToProps)(SettingsMenu)); |
@ -0,0 +1 @@ |
||||
export { default as SettingsMenu } from './SettingsMenu'; |
@ -0,0 +1 @@ |
||||
export * from './components'; |
Loading…
Reference in new issue