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 5263804f479eb10fceb65880e67a61c8fd273d44..b8635661bc8b4ada85a9155c9edd4db2d455469c 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx @@ -113,7 +113,7 @@ class AudioModal extends Component { help: { title: intl.formatMessage(intlMessages.helpTitle), component: () => this.renderHelp(), - } + }, }; } @@ -152,7 +152,7 @@ class AudioModal extends Component { this.setState({ content: 'echoTest', }); - }).catch(err => { + }).catch((err) => { if (err.type === 'MEDIA_ERROR') { this.setState({ content: 'help', @@ -166,7 +166,7 @@ class AudioModal extends Component { joinListenOnly, } = this.props; - return joinListenOnly().catch(err => { + return joinListenOnly().catch((err) => { if (err.type === 'MEDIA_ERROR') { this.setState({ content: 'help', @@ -317,8 +317,8 @@ class AudioModal extends Component { data-test="modalBaseCloseButton" className={styles.closeBtn} label={intl.formatMessage(intlMessages.closeLabel)} - icon={'close'} - size={'md'} + icon="close" + size="md" hideLabel onClick={this.closeModal} /> diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js index 81fc11b73f6d7bfa975765acfe7f99bf2853b8eb..8628984225f68a18a20c1e48f026fd323c66cbdd 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/service.js +++ b/bigbluebutton-html5/imports/ui/components/audio/service.js @@ -36,9 +36,9 @@ export default { init, exitAudio: () => AudioManager.exitAudio(), transferCall: () => AudioManager.transferCall(), - joinListenOnly: () => AudioManager.joinAudio({ isListenOnly: true }), - joinMicrophone: () => AudioManager.joinAudio(), - joinEchoTest: () => AudioManager.joinAudio({ isEchoTest: true }), + joinListenOnly: () => AudioManager.joinListenOnly(), + joinMicrophone: () => AudioManager.joinMicrophone(), + joinEchoTest: () => AudioManager.joinEchoTest(), toggleMuteMicrophone: () => AudioManager.toggleMuteMicrophone(), changeInputDevice: inputDeviceId => AudioManager.changeInputDevice(inputDeviceId), changeOutputDevice: outputDeviceId => AudioManager.changeOutputDevice(outputDeviceId), diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js index 06516e9580ef5c4d444646e4fee05b709800eae9..64374432013f4b3fd46a3264c7dc506d0ac85b4d 100644 --- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js +++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js @@ -64,49 +64,100 @@ class AudioManager { }); } - joinAudio(options = {}) { - const { - isListenOnly, - isEchoTest, - } = options; - - const permissionsTimeout = setTimeout(() => { - this.isWaitingPermissions = true; + askDevicesPermissions() { + // Only change the isWaitingPermissions for the case where the user didnt allowed it yet + const permTimeout = setTimeout(() => { + if (!this.devicesInitialized) { this.isWaitingPermissions = true; } }, 100); - const doCall = () => { - clearTimeout(permissionsTimeout); - this.isWaitingPermissions = false; - this.devicesInitialized = true; - this.isConnecting = true; - this.isMuted = false; - this.error = null; - this.isListenOnly = isListenOnly || false; - this.isEchoTest = isEchoTest || false; - - const callOptions = { - isListenOnly: this.isListenOnly, - extension: isEchoTest ? ECHO_TEST_NUMBER : null, - inputStream: this.isListenOnly ? this.createListenOnlyStream() : this.inputStream, - }; - return this.bridge.joinAudio(callOptions, this.callStateCallback.bind(this)); - }; - - if (this.devicesInitialized) return doCall(); + this.isWaitingPermissions = false; + this.devicesInitialized = false; return Promise.all([ this.setDefaultInputDevice(), this.setDefaultOutputDevice(), - ]).then(doCall) + ]).then(() => { + this.devicesInitialized = true; + this.isWaitingPermissions = false; + }).catch((err) => { + clearTimeout(permTimeout); + this.isConnecting = false; + this.isWaitingPermissions = false; + throw err; + }); + } + + joinMicrophone() { + this.isListenOnly = false; + this.isEchoTest = false; + + const callOptions = { + isListenOnly: false, + extension: null, + inputStream: this.inputStream, + }; + + return this.askDevicesPermissions() + .then(this.onAudioJoining) + .then(() => this.bridge.joinAudio(callOptions, this.callStateCallback.bind(this))); + } + + joinEchoTest() { + this.isListenOnly = false; + this.isEchoTest = true; + + const callOptions = { + isListenOnly: false, + extension: ECHO_TEST_NUMBER, + inputStream: this.inputStream, + }; + + return this.askDevicesPermissions() + .then(this.onAudioJoining) + .then(() => this.bridge.joinAudio(callOptions, this.callStateCallback.bind(this))); + } + + joinListenOnly() { + console.log('entrei'); + this.isListenOnly = true; + this.isEchoTest = false; + + const callOptions = { + isListenOnly: true, + extension: null, + inputStream: this.createListenOnlyStream(), + }; + + // We need this until we upgrade to SIP 9x. See #4690 + const iceGatheringErr = 'ICE_TIMEOUT'; + const iceGatheringTimeout = new Promise((resolve, reject) => { + setTimeout(reject, 12000, iceGatheringErr); + }); + + return this.onAudioJoining() + .then(() => Promise.race([ + this.bridge.joinAudio(callOptions, this.callStateCallback.bind(this)), + iceGatheringTimeout, + ])) .catch((err) => { - clearTimeout(permissionsTimeout); - this.isWaitingPermissions = false; - this.error = err; - this.notify(err.message); - return Promise.reject(err); + // If theres a iceGathering timeout we retry to join after asking device permissions + if (err === iceGatheringErr) { + return this.askDevicesPermissions() + .then(() => this.joinListenOnly()); + } + + throw err; }); } + onAudioJoining() { + this.isConnecting = true; + this.isMuted = false; + this.error = false; + + return Promise.resolve(); + } + exitAudio() { if (!this.isConnected) return Promise.resolve(); @@ -128,7 +179,7 @@ class AudioManager { this.isConnected = true; // listen to the VoiceUsers changes and update the flag - if(!this.muteHandle) { + if (!this.muteHandle) { const query = VoiceUsers.find({ intId: Auth.userID }); this.muteHandle = query.observeChanges({ changed: (id, fields) => { @@ -153,11 +204,9 @@ class AudioManager { this.isConnecting = false; this.isHangingUp = false; - if (!this.error && !this.isEchoTest) { this.notify(this.messages.info.LEFT_AUDIO); } - this.isEchoTest = false; } callStateCallback(response) { @@ -181,7 +230,7 @@ class AudioManager { this.onAudioExit(); } else if (status === FAILED) { this.error = error; - this.notify(this.messages.error[error]); + this.notify(this.messages.error[error], true); console.error('Audio Error:', error, bridgeError); this.onAudioExit(); } @@ -256,10 +305,10 @@ class AudioManager { return this._userData; } - notify(message) { + notify(message, error = false) { notify( message, - this.error ? 'error' : 'info', + error ? 'error' : 'info', this.isListenOnly ? 'audio_on' : 'unmute', ); }