jiti-meet/react/features/settings/components/web/audio/AudioSettingsContent.js

264 lines
7.0 KiB
JavaScript

// @flow
import React, { Component } from 'react';
import { translate } from '../../../../base/i18n';
import { IconMicrophoneEmpty, IconVolumeEmpty } from '../../../../base/icons';
import { createLocalAudioTrack } from '../../../functions';
import AudioSettingsHeader from './AudioSettingsHeader';
import MicrophoneEntry from './MicrophoneEntry';
import SpeakerEntry from './SpeakerEntry';
export type Props = {
/**
* The deviceId of the microphone in use.
*/
currentMicDeviceId: string,
/**
* The deviceId of the output device in use.
*/
currentOutputDeviceId: string,
/**
* Used to set a new microphone as the current one.
*/
setAudioInputDevice: Function,
/**
* Used to set a new output device as the current one.
*/
setAudioOutputDevice: Function,
/**
* A list of objects containing the labels and deviceIds
* of all the output devices.
*/
outputDevices: Object[],
/**
* A list with objects containing the labels and deviceIds
* of all the input devices.
*/
microphoneDevices: Object[],
/**
* Invoked to obtain translated strings.
*/
t: Function
};
type State = {
/**
* An object containing the jitsiTrack and the error (if the case)
* for the microphone that is in use.
*/
currentMicData: Object
}
/**
* Implements a React {@link Component} which displayes a list of all
* the audio input & output devices to choose from.
*
* @extends Component
*/
class AudioSettingsContent extends Component<Props, State> {
_componentWasUnmounted: boolean;
/**
* Initializes a new {@code AudioSettingsContent} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props) {
super(props);
this._onMicrophoneEntryClick = this._onMicrophoneEntryClick.bind(this);
this._onSpeakerEntryClick = this._onSpeakerEntryClick.bind(this);
this.state = {
currentMicData: {
error: false,
jitsiTrack: null
}
};
}
_onMicrophoneEntryClick: (string) => void;
/**
* Click handler for the microphone entries.
*
* @param {string} deviceId - The deviceId for the clicked microphone.
* @returns {void}
*/
_onMicrophoneEntryClick(deviceId) {
this.props.setAudioInputDevice(deviceId);
}
_onSpeakerEntryClick: (string) => void;
/**
* Click handler for the speaker entries.
*
* @param {string} deviceId - The deviceId for the clicked speaker.
* @returns {void}
*/
_onSpeakerEntryClick(deviceId) {
this.props.setAudioOutputDevice(deviceId);
}
/**
* Renders a single microphone entry.
*
* @param {Object} data - An object with the deviceId and label of the microphone.
* @param {number} index - The index of the element, used for creating a key.
* @returns {React$Node}
*/
_renderMicrophoneEntry(data, index) {
const { deviceId, label } = data;
const key = `me-${index}`;
const isSelected = deviceId === this.props.currentMicDeviceId;
let jitsiTrack = null;
let hasError = false;
if (isSelected) {
({ jitsiTrack, hasError } = this.state.currentMicData);
}
return (
<MicrophoneEntry
deviceId = { deviceId }
hasError = { hasError }
isSelected = { isSelected }
jitsiTrack = { jitsiTrack }
key = { key }
onClick = { this._onMicrophoneEntryClick }>
{label}
</MicrophoneEntry>
);
}
/**
* Renders a single speaker entry.
*
* @param {Object} data - An object with the deviceId and label of the speaker.
* @param {number} index - The index of the element, used for creating a key.
* @returns {React$Node}
*/
_renderSpeakerEntry(data, index) {
const { deviceId, label } = data;
const key = `se-${index}`;
return (
<SpeakerEntry
deviceId = { deviceId }
isSelected = { deviceId === this.props.currentOutputDeviceId }
key = { key }
onClick = { this._onSpeakerEntryClick }>
{label}
</SpeakerEntry>
);
}
/**
* Disposes the audio track for a given micData object.
*
* @param {Object} micData - The object holding the track.
* @returns {Promise<void>}
*/
_disposeTrack(micData) {
const { jitsiTrack } = micData;
return jitsiTrack ? jitsiTrack.dispose() : Promise.resolve();
}
/**
* Updates the current microphone data.
* Disposes previously created track and creates a new one.
*
* @returns {void}
*/
async _updateCurrentMicData() {
await this._disposeTrack(this.state.currentMicData);
const currentMicData = await createLocalAudioTrack(
this.props.currentMicDeviceId,
);
// In case the component gets unmounted before the track is created
// avoid a leak by not setting the state
if (this._componentWasUnmounted) {
this._disposeTrack(currentMicData);
} else {
this.setState({
currentMicData
});
}
}
/**
* Implements React's {@link Component#componentDidUpdate}.
*
* @inheritdoc
*/
componentDidUpdate(prevProps) {
if (prevProps.currentMicDeviceId !== this.props.currentMicDeviceId) {
this._updateCurrentMicData();
}
}
/**
* Implements React's {@link Component#componentDidMount}.
*
* @inheritdoc
*/
componentDidMount() {
this._updateCurrentMicData();
}
/**
* Implements React's {@link Component#componentWillUnmount}.
*
* @inheritdoc
*/
componentWillUnmount() {
this._componentWasUnmounted = true;
this._disposeTrack(this.state.currentMicData);
}
/**
* Implements React's {@link Component#render}.
*
* @inheritdoc
*/
render() {
const { microphoneDevices, outputDevices, t } = this.props;
return (
<div>
<div className = 'audio-preview-content'>
<AudioSettingsHeader
IconComponent = { IconMicrophoneEmpty }
text = { t('settings.microphones') } />
{microphoneDevices.map((data, i) =>
this._renderMicrophoneEntry(data, i),
)}
<AudioSettingsHeader
IconComponent = { IconVolumeEmpty }
text = { t('settings.speakers') } />
{outputDevices.map((data, i) =>
this._renderSpeakerEntry(data, i),
)}
</div>
</div>
);
}
}
export default translate(AudioSettingsContent);