diff --git a/bigbluebutton-html5/imports/api/users/server/methods/userLeaving.js b/bigbluebutton-html5/imports/api/users/server/methods/userLeaving.js index 733a87fa1665c966f47c42f4fee2ea3918a0b838..1ad83846ecb84898490a252c60f35fd78178f51f 100755 --- a/bigbluebutton-html5/imports/api/users/server/methods/userLeaving.js +++ b/bigbluebutton-html5/imports/api/users/server/methods/userLeaving.js @@ -23,7 +23,7 @@ export default function userLeaving(credentials, userId, connectionId) { const User = Users.findOne(selector); if (!User) { - Logger.info(`Skipping userLeaving. Could not find ${userId} in ${meetingId}`); + return Logger.info(`Skipping userLeaving. Could not find ${userId} in ${meetingId}`); } // If the current user connection is not the same that triggered the leave we skip diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx index cf75014f6e9273b4ec8f0c3433a02c381ccea75d..e2576b6e71a7f7e508a5cdf3e79606ce7d694670 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx @@ -1,9 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; +import cx from 'classnames'; import { defineMessages, intlShape, injectIntl } from 'react-intl'; import Button from '/imports/ui/components/button/component'; import { styles } from './styles'; -import cx from 'classnames'; const intlMessages = defineMessages({ joinAudio: { @@ -29,7 +29,7 @@ const propTypes = { handleJoinAudio: PropTypes.func.isRequired, handleLeaveAudio: PropTypes.func.isRequired, disable: PropTypes.bool.isRequired, - unmute: PropTypes.bool.isRequired, + unmute: PropTypes.bool, mute: PropTypes.bool.isRequired, join: PropTypes.bool.isRequired, intl: intlShape.isRequired, @@ -38,6 +38,7 @@ const propTypes = { const defaultProps = { glow: false, + unmute: false, }; const SHORTCUTS_CONFIG = Meteor.settings.public.app.shortcuts; diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx index b7d4e2fa910f8407fb9c25383701c7f341bfc3f3..547cf697386f0eb157c2db4223f89f0dd44de6eb 100755 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx @@ -356,7 +356,6 @@ class AudioModal extends Component { handleBack={this.handleGoToAudioOptions} handleRetry={this.handleRetryGoToEchoTest} joinEchoTest={this.joinEchoTest} - exitAudio={this.exitAudio} changeInputDevice={this.changeInputDevice} changeOutputDevice={this.changeOutputDevice} isConnecting={isConnecting} diff --git a/bigbluebutton-html5/imports/ui/components/audio/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/container.jsx index 893f6b507a2df504571060cac6b3b30f0df493fc..874667d8274e7c464be17a80439f12a7c0fc4e9e 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/container.jsx @@ -21,23 +21,23 @@ const intlMessages = defineMessages({ }, genericError: { id: 'app.audioManager.genericError', - description: 'Generic error messsage', + description: 'Generic error message', }, connectionError: { id: 'app.audioManager.connectionError', - description: 'Connection error messsage', + description: 'Connection error message', }, requestTimeout: { id: 'app.audioManager.requestTimeout', - description: 'Request timeout error messsage', + description: 'Request timeout error message', }, invalidTarget: { id: 'app.audioManager.invalidTarget', - description: 'Invalid target error messsage', + description: 'Invalid target error message', }, mediaError: { id: 'app.audioManager.mediaError', - description: 'Media error messsage', + description: 'Media error message', }, }); @@ -71,6 +71,12 @@ export default withModalMounter(injectIntl(withTracker(({ mountModal, intl }) => Breakouts.find().observeChanges({ removed() { + // if the user joined a breakout room, the main room's audio was + // programmatically dropped to avoid interference. On breakout end, + // offer to rejoin main room audio only if the user is not in audio already + if (Service.isUsingAudio()) { + return; + } setTimeout(() => openAudioModal(), 0); }, }); diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js index 8e15b28894171375350ed7c1292e170d84d2a928..ac03a9d7d9a42932ccfefef3ead52efc17534167 100755 --- a/bigbluebutton-html5/imports/ui/components/audio/service.js +++ b/bigbluebutton-html5/imports/ui/components/audio/service.js @@ -2,7 +2,6 @@ import Users from '/imports/api/users'; import Auth from '/imports/ui/services/auth'; import AudioManager from '/imports/ui/services/audio-manager'; import Meetings from '/imports/api/meetings'; -import VoiceUsers from '/imports/api/voice-users'; const init = (messages) => { AudioManager.setAudioMessages(messages); @@ -30,9 +29,6 @@ const init = (messages) => { AudioManager.init(userData); }; -const isVoiceUserTalking = () => - VoiceUsers.findOne({ intId: Auth.userID }).talking; - export default { init, exitAudio: () => AudioManager.exitAudio(), @@ -44,8 +40,9 @@ export default { changeInputDevice: inputDeviceId => AudioManager.changeInputDevice(inputDeviceId), changeOutputDevice: outputDeviceId => AudioManager.changeOutputDevice(outputDeviceId), isConnected: () => AudioManager.isConnected, - isTalking: () => isVoiceUserTalking(), + isTalking: () => AudioManager.isTalking, isHangingUp: () => AudioManager.isHangingUp, + isUsingAudio: () => AudioManager.isUsingAudio(), isWaitingPermissions: () => AudioManager.isWaitingPermissions, isMuted: () => AudioManager.isMuted, isConnecting: () => AudioManager.isConnecting, diff --git a/bigbluebutton-html5/imports/ui/components/notifications-bar/container.jsx b/bigbluebutton-html5/imports/ui/components/notifications-bar/container.jsx index 70866f2e6cd80830765e23c91c29b7e12bd61958..063e720d58965837a24f241a2d4fbcf54dc65e12 100644 --- a/bigbluebutton-html5/imports/ui/components/notifications-bar/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/notifications-bar/container.jsx @@ -83,19 +83,9 @@ const setRetrySeconds = (sec = 0) => { } }; -const changeDocumentTitle = (sec) => { - if (sec >= 0) { - const affix = `(${humanizeSeconds(sec)}`; - const splitTitle = document.title.split(') '); - const title = splitTitle[1] || splitTitle[0]; - document.title = [affix, title].join(') '); - } -}; - const setTimeRemaining = (sec = 0) => { if (sec !== timeRemaining) { timeRemaining = sec; - changeDocumentTitle(sec); timeRemainingDep.changed(); } }; diff --git a/bigbluebutton-html5/imports/ui/components/toast/container.jsx b/bigbluebutton-html5/imports/ui/components/toast/container.jsx index 59242e12224ef5f4120f0be657c741136d7523d1..8d0ee1ab29ee04ce358d42b7f66cd8d7617eadd7 100644 --- a/bigbluebutton-html5/imports/ui/components/toast/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/toast/container.jsx @@ -10,6 +10,7 @@ import Meetings from '/imports/api/meetings'; import Icon from '../icon/component'; import { styles } from './styles'; +import AudioService from '../audio/service'; const intlMessages = defineMessages({ @@ -41,7 +42,9 @@ class ToastContainer extends React.Component { export default injectIntl(injectNotify(withTracker(({ notify, intl }) => { Breakouts.find().observeChanges({ removed() { - notify(intl.formatMessage(intlMessages.toastBreakoutRoomEnded), 'info', 'rooms'); + if (!AudioService.isUsingAudio()) { + notify(intl.formatMessage(intlMessages.toastBreakoutRoomEnded), 'info', 'rooms'); + } }, }); diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js index ee08c89cf9a025323670a60c9978a5b91ba9c90c..d432835a0e68325edd684ad3829fd5dbef4615ad 100755 --- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js +++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js @@ -32,6 +32,7 @@ class AudioManager { isHangingUp: false, isListenOnly: false, isEchoTest: false, + isTalking: false, isWaitingPermissions: false, error: null, outputDeviceId: null, @@ -173,6 +174,8 @@ class AudioManager { const bridge = USE_KURENTO? this.listenOnlyBridge : this.bridge; this.isHangingUp = true; + this.isEchoTest = false; + return bridge.exitAudio(); } @@ -194,8 +197,17 @@ class AudioManager { const query = VoiceUsers.find({ intId: Auth.userID }); this.muteHandle = query.observeChanges({ changed: (id, fields) => { - if (fields.muted === this.isMuted) return; - this.isMuted = fields.muted; + if (fields.muted !== undefined && fields.muted !== this.isMuted) { + this.isMuted = fields.muted; + } + + if (fields.talking !== undefined && fields.talking !== this.isTalking) { + this.isTalking = fields.talking; + } + + if (this.isMuted) { + this.isTalking = false; + } }, }); } @@ -214,6 +226,7 @@ class AudioManager { this.isConnected = false; this.isConnecting = false; this.isHangingUp = false; + this.isListenOnly = false; if (this.inputStream) { window.defaultInputStream.forEach(track => track.stop()); @@ -267,6 +280,11 @@ class AudioManager { return this.listenOnlyAudioContext.createMediaStreamDestination().stream; } + isUsingAudio() { + return this.isConnected || this.isConnecting || + this.isHangingUp || this.isEchoTest; + } + setDefaultInputDevice() { return this.changeInputDevice(); }