import React from 'react'; import PropTypes from 'prop-types'; import { defineMessages, injectIntl } from 'react-intl'; import Button from '/imports/ui/components/common/button/component'; import { withModalMounter } from '/imports/ui/components/common/modal/service'; import AudioTestContainer from '/imports/ui/components/audio/audio-test/container'; import Styled from './styles'; import logger from '/imports/startup/client/logger'; import AudioStreamVolume from '/imports/ui/components/audio/audio-stream-volume/component'; import LocalEcho from '/imports/ui/components/audio/local-echo/component'; import { getAudioConstraints, } from '/imports/api/audio/client/bridge/service'; import MediaStreamUtils from '/imports/utils/media-stream-utils'; const propTypes = { intl: PropTypes.shape({ formatMessage: PropTypes.func.isRequired, }).isRequired, changeInputDevice: PropTypes.func.isRequired, changeOutputDevice: PropTypes.func.isRequired, handleBack: PropTypes.func.isRequired, handleConfirmation: PropTypes.func.isRequired, handleGUMFailure: PropTypes.func.isRequired, isConnecting: PropTypes.bool.isRequired, inputDeviceId: PropTypes.string.isRequired, outputDeviceId: PropTypes.string.isRequired, withEcho: PropTypes.bool.isRequired, withVolumeMeter: PropTypes.bool.isRequired, }; const intlMessages = defineMessages({ backLabel: { id: 'app.audio.backLabel', description: 'audio settings back button label', }, descriptionLabel: { id: 'app.audio.audioSettings.descriptionLabel', description: 'audio settings description label', }, micSourceLabel: { id: 'app.audio.audioSettings.microphoneSourceLabel', description: 'Label for mic source', }, speakerSourceLabel: { id: 'app.audio.audioSettings.speakerSourceLabel', description: 'Label for speaker source', }, testSpeakerLabel: { id: 'app.audio.audioSettings.testSpeakerLabel', description: 'Label for the speaker test button', }, streamVolumeLabel: { id: 'app.audio.audioSettings.microphoneStreamLabel', description: 'Label for stream volume', }, retryLabel: { id: 'app.audio.joinAudio', description: 'Confirmation button label', }, }); class AudioSettings extends React.Component { constructor(props) { super(props); const { inputDeviceId, outputDeviceId, } = props; this.handleInputChange = this.handleInputChange.bind(this); this.handleOutputChange = this.handleOutputChange.bind(this); this.state = { inputDeviceId, outputDeviceId, stream: null, }; this._isMounted = false; } componentDidMount() { const { inputDeviceId } = this.state; this._isMounted = true; this.handleInputChange(inputDeviceId); } componentWillUnmount() { const { stream } = this.state; this._mounted = false; if (stream) { MediaStreamUtils.stopMediaStreamTracks(stream); } } generateInputStream(inputDeviceId) { const { stream } = this.state; if (inputDeviceId && stream) { const currentDeviceId = MediaStreamUtils.extractDeviceIdFromStream(stream, 'audio'); if (currentDeviceId === inputDeviceId) return Promise.resolve(stream); MediaStreamUtils.stopMediaStreamTracks(stream); } const constraints = { audio: getAudioConstraints({ deviceId: inputDeviceId }), }; return navigator.mediaDevices.getUserMedia(constraints) } handleInputChange(deviceId) { const { handleGUMFailure, changeInputDevice, withEcho, withVolumeMeter, } = this.props; changeInputDevice(deviceId); // Only generate input streams if they're going to be used with something // In this case, the volume meter or local echo test. if (withEcho || withVolumeMeter) { this.generateInputStream(deviceId).then((stream) => { if (!this._isMounted) return; this.setState({ inputDeviceId: deviceId, stream, }); }).catch((error) => { logger.warn({ logCode: 'audiosettings_gum_failed', extraInfo: { deviceId, errorMessage: error.message, errorName: error.name, }, }, `Audio settings gUM failed: ${error.name}`); handleGUMFailure(error); }); } else { this.setState({ inputDeviceId: deviceId, }); } } handleOutputChange(deviceId) { const { changeOutputDevice, withEcho, } = this.props; changeOutputDevice(deviceId, withEcho); this.setState({ outputDeviceId: deviceId, }); } renderOutputTest() { const { withEcho, intl } = this.props; const { stream } = this.state; return ( {intl.formatMessage(intlMessages.testSpeakerLabel)} {!withEcho ? : } ); } renderVolumeMeter() { const { withVolumeMeter, intl } = this.props; const { stream } = this.state; return withVolumeMeter ? ( {intl.formatMessage(intlMessages.streamVolumeLabel)} ) : null } render() { const { isConnecting, intl, handleBack, handleConfirmation, } = this.props; const { inputDeviceId, outputDeviceId } = this.state; return ( {intl.formatMessage(intlMessages.descriptionLabel)} {intl.formatMessage(intlMessages.micSourceLabel)} {intl.formatMessage(intlMessages.speakerSourceLabel)} {this.renderOutputTest()} {this.renderVolumeMeter()}