diff --git a/bigbluebutton-html5/imports/api/users-settings/server/methods/addUserSettings.js b/bigbluebutton-html5/imports/api/users-settings/server/methods/addUserSettings.js index 68e9682bb7..274a546e39 100644 --- a/bigbluebutton-html5/imports/api/users-settings/server/methods/addUserSettings.js +++ b/bigbluebutton-html5/imports/api/users-settings/server/methods/addUserSettings.js @@ -64,6 +64,7 @@ const currentParameters = [ 'bbb_hide_actions_bar', 'bbb_hide_nav_bar', 'bbb_change_layout', + 'bbb_direct_leave_button', ]; function valueParser(val) { diff --git a/bigbluebutton-html5/imports/ui/components/common/button/styles.js b/bigbluebutton-html5/imports/ui/components/common/button/styles.js index 80bc304ed5..1f1f8e0078 100644 --- a/bigbluebutton-html5/imports/ui/components/common/button/styles.js +++ b/bigbluebutton-html5/imports/ui/components/common/button/styles.js @@ -42,6 +42,7 @@ import { btnDangerBorder, btnDangerColor, btnDangerBg, + btnDangerBgHover, btnDarkBorder, btnDarkColor, btnDarkBg, @@ -924,6 +925,7 @@ const Button = styled(BaseButton)` &:hover, .buttonWrapper:hover & { color: ${btnDangerColor}; + background-color: ${btnDangerBgHover}; } `} diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx index 38ad6f585d..47d5f9d34b 100755 --- a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx @@ -14,6 +14,7 @@ import browserInfo from '/imports/utils/browserInfo'; import deviceInfo from '/imports/utils/deviceInfo'; import { PANELS, ACTIONS } from '../layout/enums'; import { isEqual } from 'radash'; +import LeaveMeetingButton from './leave-meeting-button/component'; const intlMessages = defineMessages({ toggleUserListLabel: { @@ -36,6 +37,10 @@ const intlMessages = defineMessages({ id: 'app.createBreakoutRoom.room', description: 'default breakout room name', }, + leaveMeetingLabel: { + id: 'app.navBar.leaveMeetingBtnLabel', + description: 'Leave meeting button label', + }, }); const propTypes = { @@ -170,6 +175,8 @@ class NavBar extends Component { isPinned, sidebarNavigation, currentUserId, + isDirectLeaveButtonEnabled, + isMeteorConnected, } = this.props; const hasNotification = hasUnreadMessages || (hasUnreadNotes && !isPinned); @@ -245,7 +252,13 @@ class NavBar extends Component { {ConnectionStatusService.isEnabled() ? : null} - + {isDirectLeaveButtonEnabled && isMeteorConnected + ? + : null} + diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx index 0d606df111..9ad72d4952 100755 --- a/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx @@ -114,6 +114,11 @@ export default withTracker(() => { } } + const IS_DIRECT_LEAVE_BUTTON_ENABLED = getFromUserSettings( + 'bbb_direct_leave_button', + PUBLIC_CONFIG.app.defaultSettings.application.directLeaveButton, + ); + return { isPinned: NotesService.isSharedNotesPinned(), currentUserId: Auth.userID, @@ -123,5 +128,7 @@ export default withTracker(() => { breakoutName, meetingName, unread, + isDirectLeaveButtonEnabled: IS_DIRECT_LEAVE_BUTTON_ENABLED, + isMeteorConnected: Meteor.status().connected, }; -})(NavBarContainer); \ No newline at end of file +})(NavBarContainer); diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/leave-meeting-button/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/leave-meeting-button/component.jsx new file mode 100644 index 0000000000..e42da4e9e2 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/leave-meeting-button/component.jsx @@ -0,0 +1,34 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Styled from './styles'; +import { makeCall } from '/imports/ui/services/api'; + +// Set the logout code to 680 because it's not a real code and can be matched on the other side +const LOGOUT_CODE = '680'; + +const propTypes = { + label: PropTypes.string.isRequired, +}; + +const LeaveMeetingButton = ({ label }) => { + const leaveSession = () => { + makeCall('userLeftMeeting'); + // we don't check askForFeedbackOnLogout here, + // it is checked in meeting-ended component + Session.set('codeError', LOGOUT_CODE); + }; + + return ( + leaveSession()} + icon="logout" + /> + ); +}; + +LeaveMeetingButton.propTypes = propTypes; + +export default LeaveMeetingButton; diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/leave-meeting-button/styles.js b/bigbluebutton-html5/imports/ui/components/nav-bar/leave-meeting-button/styles.js new file mode 100644 index 0000000000..b9017db50a --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/leave-meeting-button/styles.js @@ -0,0 +1,13 @@ +import styled from 'styled-components'; +import Button from '/imports/ui/components/common/button/component'; + +const LeaveButton = styled(Button)` + border-radius: 1.1rem; + font-size: 1rem; + line-height: 1.1rem; + font-weight: 400; +`; + +export default { + LeaveButton, +}; diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/settings-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/settings-dropdown/component.jsx index 93be022538..ed4a271263 100644 --- a/bigbluebutton-html5/imports/ui/components/nav-bar/settings-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/settings-dropdown/component.jsx @@ -118,6 +118,7 @@ const propTypes = { audioCaptionsActive: PropTypes.bool.isRequired, audioCaptionsSet: PropTypes.func.isRequired, isMobile: PropTypes.bool.isRequired, + isDirectLeaveButtonEnabled: PropTypes.bool.isRequired, }; const defaultProps = { @@ -243,7 +244,7 @@ class SettingsDropdown extends PureComponent { renderMenuItems() { const { intl, amIModerator, isBreakoutRoom, isMeteorConnected, audioCaptionsEnabled, - audioCaptionsActive, audioCaptionsSet, isMobile, + audioCaptionsActive, audioCaptionsSet, isMobile, isDirectLeaveButtonEnabled, } = this.props; const { isIos } = deviceInfo; @@ -341,7 +342,7 @@ class SettingsDropdown extends PureComponent { }, ); - if (allowLogoutSetting && isMeteorConnected) { + if (allowLogoutSetting && isMeteorConnected && !isDirectLeaveButtonEnabled) { this.menuItems.push( { key: 'list-item-logout', diff --git a/bigbluebutton-html5/imports/ui/stylesheets/styled-components/palette.js b/bigbluebutton-html5/imports/ui/stylesheets/styled-components/palette.js index 97d26de365..4d0d1c068c 100644 --- a/bigbluebutton-html5/imports/ui/stylesheets/styled-components/palette.js +++ b/bigbluebutton-html5/imports/ui/stylesheets/styled-components/palette.js @@ -64,6 +64,7 @@ const btnWarningBg = `var(--btn-warning-bg, ${colorWarning})`; const btnDangerBorder = `var(--btn-danger-border, ${colorDanger})`; const btnDangerColor = `var(--btn-danger-color, ${colorWhite})`; const btnDangerBg = `var(--btn-danger-bg, ${colorDanger})`; +const btnDangerBgHover = 'var(--btn-danger-bg-hover, #C61C1C)'; const btnDarkBorder = `var(--btn-dark-border, ${colorDanger})`; const btnDarkColor = `var(--btn-dark-color, ${colorWhite})`; @@ -171,6 +172,7 @@ export { btnDangerBorder, btnDangerColor, btnDangerBg, + btnDangerBgHover, btnDarkBorder, btnDarkColor, btnDarkBg, diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml index 4193a4ffc9..70611b3da1 100755 --- a/bigbluebutton-html5/private/config/settings.yml +++ b/bigbluebutton-html5/private/config/settings.yml @@ -200,6 +200,7 @@ public: paginationEnabled: true whiteboardToolbarAutoHide: false autoCloseReactionsBar: true + directLeaveButton: false darkTheme: false # fallbackLocale: if the locale the client is loaded in does not have a # translation a string, it will use the translation from the locale diff --git a/bigbluebutton-html5/public/locales/en.json b/bigbluebutton-html5/public/locales/en.json index 79c8d90965..16f451334b 100755 --- a/bigbluebutton-html5/public/locales/en.json +++ b/bigbluebutton-html5/public/locales/en.json @@ -436,6 +436,7 @@ "app.muteWarning.label": "Click {0} to unmute yourself.", "app.muteWarning.disableMessage": "Mute alerts disabled until unmute", "app.muteWarning.tooltip": "Click to close and disable warning until next unmute", + "app.navBar.leaveMeetingBtnLabel": "Leave", "app.navBar.settingsDropdown.optionsLabel": "Options", "app.navBar.settingsDropdown.fullscreenLabel": "Fullscreen Application", "app.navBar.settingsDropdown.settingsLabel": "Settings", diff --git a/docs/docs/administration/customize.md b/docs/docs/administration/customize.md index 536bab4524..776d502fdb 100644 --- a/docs/docs/administration/customize.md +++ b/docs/docs/administration/customize.md @@ -1432,6 +1432,7 @@ Useful tools for development: | `userdata-bbb_skip_check_audio_on_first_join=` | (Introduced in BigBlueButton 2.3) If set to `true`, the user will not see the "echo test" when sharing audio for the first time in the session. If the user stops sharing, next time they try to share audio the echo test window will be displayed, allowing for configuration changes to be made prior to sharing audio again | `false` | | `userdata-bbb_override_default_locale=` | (Introduced in BigBlueButton 2.3) If set to `de`, the user's browser preference will be ignored - the client will be shown in 'de' (i.e. German) regardless of the otherwise preferred locale 'en' (or other) | `null` | | `userdata-bbb_hide_presentation_on_join` | (Introduced in BigBlueButton 2.6) If set to `true` it will make the user enter the meeting with presentation minimized (Only for non-presenters), not peremanent. | `false` | +| `userdata-bbb_direct_leave_button` | (Introduced in BigBlueButton 2.7) If set to `true` it will make a button to leave the meeting appear to the left of the Options menu. | `false` | #### Branding parameters