import React, { Component } from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; import Modal from '/imports/ui/components/modal/simple/component'; import Button from '/imports/ui/components/button/component'; import { Session } from 'meteor/session'; import { defineMessages, injectIntl, FormattedMessage, } from 'react-intl'; import { styles } from './styles'; import PermissionsOverlay from '../permissions-overlay/component'; import AudioSettings from '../audio-settings/component'; import EchoTest from '../echo-test/component'; import Help from '../help/component'; import AudioDial from '../audio-dial/component'; import AudioAutoplayPrompt from '../autoplay/component'; const propTypes = { intl: PropTypes.object.isRequired, closeModal: PropTypes.func.isRequired, joinMicrophone: PropTypes.func.isRequired, joinListenOnly: PropTypes.func.isRequired, joinEchoTest: PropTypes.func.isRequired, exitAudio: PropTypes.func.isRequired, leaveEchoTest: PropTypes.func.isRequired, changeInputDevice: PropTypes.func.isRequired, changeOutputDevice: PropTypes.func.isRequired, isEchoTest: PropTypes.bool.isRequired, isConnecting: PropTypes.bool.isRequired, isConnected: PropTypes.bool.isRequired, inputDeviceId: PropTypes.string, outputDeviceId: PropTypes.string, formattedDialNum: PropTypes.string.isRequired, showPermissionsOvelay: PropTypes.bool.isRequired, listenOnlyMode: PropTypes.bool.isRequired, joinFullAudioImmediately: PropTypes.bool, forceListenOnlyAttendee: PropTypes.bool.isRequired, audioLocked: PropTypes.bool.isRequired, resolve: PropTypes.func, isMobileNative: PropTypes.bool.isRequired, isIOSChrome: PropTypes.bool.isRequired, isIEOrEdge: PropTypes.bool.isRequired, formattedTelVoice: PropTypes.string.isRequired, autoplayBlocked: PropTypes.bool.isRequired, handleAllowAutoplay: PropTypes.func.isRequired, }; const defaultProps = { inputDeviceId: null, outputDeviceId: null, resolve: null, joinFullAudioImmediately: false, }; const intlMessages = defineMessages({ microphoneLabel: { id: 'app.audioModal.microphoneLabel', description: 'Join mic audio button label', }, listenOnlyLabel: { id: 'app.audioModal.listenOnlyLabel', description: 'Join listen only audio button label', }, closeLabel: { id: 'app.audioModal.closeLabel', description: 'close audio modal button label', }, audioChoiceLabel: { id: 'app.audioModal.audioChoiceLabel', description: 'Join audio modal title', }, iOSError: { id: 'app.audioModal.iOSBrowser', description: 'Audio/Video Not supported warning', }, iOSErrorDescription: { id: 'app.audioModal.iOSErrorDescription', description: 'Audio/Video not supported description', }, iOSErrorRecommendation: { id: 'app.audioModal.iOSErrorRecommendation', description: 'Audio/Video recommended action', }, echoTestTitle: { id: 'app.audioModal.echoTestTitle', description: 'Title for the echo test', }, settingsTitle: { id: 'app.audioModal.settingsTitle', description: 'Title for the audio modal', }, helpTitle: { id: 'app.audioModal.helpTitle', description: 'Title for the audio help', }, audioDialTitle: { id: 'app.audioModal.audioDialTitle', description: 'Title for the audio dial', }, connecting: { id: 'app.audioModal.connecting', description: 'Message for audio connecting', }, connectingEchoTest: { id: 'app.audioModal.connectingEchoTest', description: 'Message for echo test connecting', }, ariaModalTitle: { id: 'app.audioModal.ariaTitle', description: 'aria label for modal title', }, autoplayPromptTitle: { id: 'app.audioModal.autoplayBlockedDesc', description: 'Message for autoplay audio block', }, }); class AudioModal extends Component { constructor(props) { super(props); this.state = { content: null, hasError: false, errCode: null, }; this.handleGoToAudioOptions = this.handleGoToAudioOptions.bind(this); this.handleGoToAudioSettings = this.handleGoToAudioSettings.bind(this); this.handleRetryGoToEchoTest = this.handleRetryGoToEchoTest.bind(this); this.handleGoToEchoTest = this.handleGoToEchoTest.bind(this); this.handleJoinMicrophone = this.handleJoinMicrophone.bind(this); this.handleJoinListenOnly = this.handleJoinListenOnly.bind(this); this.skipAudioOptions = this.skipAudioOptions.bind(this); this.contents = { echoTest: { title: intlMessages.echoTestTitle, component: () => this.renderEchoTest(), }, settings: { title: intlMessages.settingsTitle, component: () => this.renderAudioSettings(), }, help: { title: intlMessages.helpTitle, component: () => this.renderHelp(), }, audioDial: { title: intlMessages.audioDialTitle, component: () => this.renderAudioDial(), }, autoplayBlocked: { title: intlMessages.autoplayPromptTitle, component: () => this.renderAutoplayOverlay(), }, }; this.failedMediaElements = []; } componentDidMount() { const { forceListenOnlyAttendee, audioLocked, } = this.props; if (forceListenOnlyAttendee) return this.handleJoinListenOnly(); if (audioLocked) return this.handleJoinMicrophone(); } componentDidUpdate(prevProps) { const { autoplayBlocked, closeModal } = this.props; if (autoplayBlocked !== prevProps.autoplayBlocked) { autoplayBlocked ? this.setState({ content: 'autoplayBlocked' }) : closeModal(); } } componentWillUnmount() { const { isEchoTest, exitAudio, resolve, } = this.props; if (isEchoTest) { exitAudio(); } if (resolve) resolve(); Session.set('audioModalIsOpen', false); } handleGoToAudioOptions() { this.setState({ content: null, hasError: true, disableActions: false, }); } handleGoToAudioSettings() { const { leaveEchoTest } = this.props; leaveEchoTest().then(() => { this.setState({ content: 'settings', }); }); } handleRetryGoToEchoTest() { this.setState({ hasError: false, content: null, }); return this.handleGoToEchoTest(); } 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 { joinEchoTest, isConnecting, } = this.props; const { disableActions, } = this.state; if (disableActions && isConnecting) return; this.setState({ hasError: false, disableActions: true, }); return joinEchoTest().then(() => { this.setState({ content: 'echoTest', disableActions: false, }); }).catch((err) => { const { type } = err; switch (type) { case 'MEDIA_ERROR': this.setState({ content: 'help', errCode: 0, disableActions: false, }); break; case 'CONNECTION_ERROR': this.setState({ errCode: 0, disableActions: false, }); break; default: this.setState({ errCode: 0, disableActions: false, }); break; } }); } handleJoinListenOnly() { const { joinListenOnly, } = this.props; const { disableActions, } = this.state; if (disableActions) return; this.setState({ disableActions: true, }); return joinListenOnly().then(() => { this.setState({ disableActions: false, }); }).catch((err) => { if (err.type === 'MEDIA_ERROR') { this.setState({ content: 'help', }); } }); } handleJoinMicrophone() { const { joinMicrophone, } = this.props; const { disableActions, } = this.state; if (disableActions) return; this.setState({ hasError: false, disableActions: true, }); joinMicrophone().then(() => { this.setState({ disableActions: false, }); }).catch(this.handleGoToAudioOptions); } skipAudioOptions() { const { isConnecting, } = this.props; const { content, hasError, } = this.state; return isConnecting && !content && !hasError; } renderAudioOptions() { const { intl, listenOnlyMode, forceListenOnlyAttendee, joinFullAudioImmediately, audioLocked, isMobileNative, formattedDialNum, isRTL, } = this.props; const showMicrophone = forceListenOnlyAttendee || audioLocked; const arrow = isRTL ? '←' : '→'; const dialAudioLabel = `${intl.formatMessage(intlMessages.audioDialTitle)} ${arrow}`; return (
{!showMicrophone && !isMobileNative ? (
); } renderContent() { const { isEchoTest, intl, isIOSChrome, } = this.props; const { content } = this.state; if (isIOSChrome) { return (
!

{intl.formatMessage(intlMessages.iOSError)}

{intl.formatMessage(intlMessages.iOSErrorDescription)}
{intl.formatMessage(intlMessages.iOSErrorRecommendation)}
); } if (this.skipAudioOptions()) { return (
{!isEchoTest ? intl.formatMessage(intlMessages.connecting) : intl.formatMessage(intlMessages.connectingEchoTest) }
); } return content ? this.contents[content].component() : this.renderAudioOptions(); } renderEchoTest() { return ( ); } renderAudioSettings() { const { isConnecting, isConnected, isEchoTest, inputDeviceId, outputDeviceId, joinEchoTest, changeInputDevice, changeOutputDevice, } = this.props; return ( ); } renderHelp() { const { errCode } = this.state; const { AudioError } = this.props; const audioErr = { ...AudioError, code: errCode, }; return ( ); } renderAudioDial() { const { formattedDialNum, formattedTelVoice } = this.props; return ( ); } renderAutoplayOverlay() { const { handleAllowAutoplay } = this.props; return ( ); } render() { const { intl, showPermissionsOvelay, isIOSChrome, closeModal, isIEOrEdge, } = this.props; const { content } = this.state; return ( {showPermissionsOvelay ? : null} {isIEOrEdge ? (

Chrome, 1: Firefox, }} />

) : null} {!this.skipAudioOptions() ? (
{ isIOSChrome ? null : (

{content ? intl.formatMessage(this.contents[content].title) : intl.formatMessage(intlMessages.audioChoiceLabel)}

) }
) : null }
{this.renderContent()}
); } } AudioModal.propTypes = propTypes; AudioModal.defaultProps = defaultProps; export default injectIntl(AudioModal);