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 1aec01b470b9c77cd4b523cc9ec88c8dba767963..4538c3a3573e638b9f35a8a08c5c8019a6dc9385 100755 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx @@ -124,6 +124,7 @@ class AudioModal extends Component { this.state = { content: null, hasError: false, + errCode: null, }; this.handleGoToAudioOptions = this.handleGoToAudioOptions.bind(this); @@ -231,6 +232,17 @@ class AudioModal extends Component { } handleGoToEchoTest() { + const { AudioError } = this.props; + const { MIC_ERROR } = AudioError; + const noSSL = !window.location.protocol.includes('https'); + + if (noSSL) { + return this.setState({ + content: 'help', + errCode: MIC_ERROR.NO_SSL, + }); + } + const { inputDeviceId, outputDeviceId, @@ -250,6 +262,7 @@ class AudioModal extends Component { if (err.type === 'MEDIA_ERROR') { this.setState({ content: 'help', + errCode: err.code, }); } }); @@ -371,12 +384,12 @@ class AudioModal extends Component { const { isEchoTest, intl, - hasMediaDevices, + isIOSChrome, } = this.props; const { content } = this.state; - if (!hasMediaDevices) { + if (isIOSChrome) { return ( <div> <div className={styles.warning}>!</div> @@ -387,6 +400,7 @@ class AudioModal extends Component { </div> </div>); } + if (this.skipAudioOptions()) { return ( <div className={styles.connecting} role="alert"> @@ -441,9 +455,18 @@ class AudioModal extends Component { } renderHelp() { + const { errCode } = this.state; + const { AudioError } = this.props; + + const audioErr = { + ...AudioError, + code: errCode, + }; + return ( <Help handleBack={this.handleGoToAudioOptions} + audioErr={audioErr} /> ); } @@ -502,7 +525,6 @@ class AudioModal extends Component { </p> ) : null} {!this.skipAudioOptions() - ? ( <header data-test="audioModalHeader" diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx index 94b88889a3a87d7b965dfd08565c9c8c9851b0d3..13e433567500a57d17c0661b92c1f8a0ecc847c3 100755 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx @@ -8,6 +8,7 @@ import Meetings from '/imports/api/meetings'; import Auth from '/imports/ui/services/auth'; import deviceInfo from '/imports/utils/deviceInfo'; import lockContextContainer from '/imports/ui/components/lock-viewers/context/container'; +import AudioError from '/imports/ui/services/audio-manager/error-codes'; import Service from '../service'; const AudioModalContainer = props => <AudioModal {...props} />; @@ -17,7 +18,6 @@ const APP_CONFIG = Meteor.settings.public.app; const invalidDialNumbers = ['0', '613-555-1212', '613-555-1234', '0000']; const isRTL = document.documentElement.getAttribute('dir') === 'rtl'; - export default lockContextContainer(withModalMounter(withTracker(({ mountModal, userLocks }) => { const listenOnlyMode = getFromUserSettings('listenOnlyMode', APP_CONFIG.listenOnlyMode); const forceListenOnly = getFromUserSettings('forceListenOnly', APP_CONFIG.forceListenOnly); @@ -106,5 +106,6 @@ export default lockContextContainer(withModalMounter(withTracker(({ mountModal, autoplayBlocked: Service.autoplayBlocked(), handleAllowAutoplay: () => Service.handleAllowAutoplay(), isRTL, + AudioError, }); })(AudioModalContainer))); diff --git a/bigbluebutton-html5/imports/ui/components/audio/help/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/help/component.jsx index c5c0cd3b0b181245d857f2b7593da694488cb6ab..4bdc51681e586c12aa5d76ad3a39d188b9ce8338 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/help/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/help/component.jsx @@ -11,34 +11,60 @@ const intlMessages = defineMessages({ backLabel: { id: 'app.audio.backLabel', description: 'audio settings back button label', - } -}) + }, + noSSL: { + id: 'app.audioModal.help.noSSL', + description: 'Text decription for domain not using https', + }, + macNotAllowed: { + id: 'app.audioModal.help.macNotAllowed', + description: 'Text decription for mac needed to enable OS setting', + }, +}); class Help extends Component { render() { const { intl, handleBack, + audioErr, } = this.props; + const { code, MIC_ERROR } = audioErr; + + let helpMessage = null; + + switch (code) { + case MIC_ERROR.NO_SSL: + helpMessage = intl.formatMessage(intlMessages.noSSL); + break; + case MIC_ERROR.MAC_OS_BLOCK: + helpMessage = intl.formatMessage(intlMessages.macNotAllowed); + break; + case MIC_ERROR.NO_PERMISSION: + default: + helpMessage = intl.formatMessage(intlMessages.descriptionHelp); + break; + } + return ( <span className={styles.help}> <div className={styles.text}> - { intl.formatMessage(intlMessages.descriptionHelp) } + { helpMessage } </div> <div className={styles.enterAudio}> <Button className={styles.backBtn} label={intl.formatMessage(intlMessages.backLabel)} - size={'md'} - color={'primary'} + size="md" + color="primary" onClick={handleBack} ghost /> </div> </span> - ) + ); } -}; +} export default injectIntl(Help); diff --git a/bigbluebutton-html5/imports/ui/components/audio/help/styles.scss b/bigbluebutton-html5/imports/ui/components/audio/help/styles.scss index 97e7b5202eb2c275fa18c7c19cc44b389ee3082c..9a7489785e04fa23aa27edb083845bff4f878c1d 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/help/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/audio/help/styles.scss @@ -1,8 +1,13 @@ @import "/imports/ui/stylesheets/variables/_all"; +:root { + --help-err-height: 10rem; +} + .help { display: flex; flex-flow: column; + height: var(--help-err-height); } .text { @@ -22,7 +27,7 @@ } @include mq($small-only) { - margin-right: auto; + margin-right: none; margin-left: inherit; [dir="rtl"] & { diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/error-codes.js b/bigbluebutton-html5/imports/ui/services/audio-manager/error-codes.js new file mode 100644 index 0000000000000000000000000000000000000000..7e8fede881bded9620fd07e365f7465efc8f0f66 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/services/audio-manager/error-codes.js @@ -0,0 +1,7 @@ +const MIC_ERROR = { + NO_SSL: 9, + MAC_OS_BLOCK: 8, + NO_PERMISSION: 7, +}; + +export default { MIC_ERROR }; diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js index 3f82c97d3c5f9494a0d076caa5ef60787ad44aca..442699f6c9e934c2574532f7ba2ff27a81780b2b 100755 --- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js +++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js @@ -6,9 +6,10 @@ import SIPBridge from '/imports/api/audio/client/bridge/sip'; import logger from '/imports/startup/client/logger'; import { notify } from '/imports/ui/services/notification'; import browser from 'browser-detect'; +import playAndRetry from '/imports/utils/mediaElementPlayRetry'; import iosWebviewAudioPolyfills from '../../../utils/ios-webview-audio-polyfills'; import { tryGenerateIceCandidates } from '../../../utils/safari-webrtc'; -import playAndRetry from '/imports/utils/mediaElementPlayRetry'; +import AudioErrors from './error-codes'; const MEDIA = Meteor.settings.public.media; const MEDIA_TAG = MEDIA.mediaTag; @@ -432,9 +433,18 @@ class AudioManager { errorMessage: error.message, }, }, `Error getting microphone - {${error.name}: ${error.message}}`); + + const { MIC_ERROR } = AudioErrors; + const disabledSysSetting = error.message.includes('Permission denied by system'); + const isMac = navigator.platform.indexOf('Mac') !== -1; + + let code = MIC_ERROR.NO_PERMISSION; + if (isMac && disabledSysSetting) code = MIC_ERROR.MAC_OS_BLOCK; + return Promise.reject({ type: 'MEDIA_ERROR', message: this.messages.error.MEDIA_ERROR, + code, }); }; diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json index daa33dbe42378d828de48faadfd553b532fabaca..95f54c8c10c9b1104b643a999ac144734dddd759 100755 --- a/bigbluebutton-html5/private/locales/en.json +++ b/bigbluebutton-html5/private/locales/en.json @@ -398,6 +398,8 @@ "app.audioModal.settingsTitle": "Change your audio settings", "app.audioModal.helpTitle": "There was an issue with your media devices", "app.audioModal.helpText": "Did you give permission for access to your microphone? Note that a dialog should appear when you try to join audio, asking for your media device permissions, please accept that in order to join the audio conference. If that is not the case, try changing your microphone permissions in your browser's settings.", + "app.audioModal.help.noSSL": "This page is unsecured. For microphone access to be allowed the page must be served over HTTPS. Please contact the server administrator.", + "app.audioModal.help.macNotAllowed": "It looks like your Mac System Preferences are blocking access to your microphone. Open System Preferences > Security & Privacy > Privacy > Microphone, and verify that the browser you're using is checked.", "app.audioModal.audioDialTitle": "Join using your phone", "app.audioDial.audioDialDescription": "Dial", "app.audioDial.audioDialConfrenceText": "and enter the conference PIN number:",