fix(settings): Always show mic audio levels

This commit is contained in:
Vlad Piersec 2020-06-25 13:31:14 +03:00 committed by Jaya Allamsetty
parent 4b1743bb2f
commit 99e7d636b7
2 changed files with 74 additions and 69 deletions

View File

@ -4,7 +4,8 @@ import React, { Component } from 'react';
import { translate } from '../../../../base/i18n'; import { translate } from '../../../../base/i18n';
import { IconMicrophoneEmpty, IconVolumeEmpty } from '../../../../base/icons'; import { IconMicrophoneEmpty, IconVolumeEmpty } from '../../../../base/icons';
import { createLocalAudioTrack } from '../../../functions'; import { equals } from '../../../../base/redux';
import { createLocalAudioTracks } from '../../../functions';
import AudioSettingsHeader from './AudioSettingsHeader'; import AudioSettingsHeader from './AudioSettingsHeader';
import MicrophoneEntry from './MicrophoneEntry'; import MicrophoneEntry from './MicrophoneEntry';
@ -53,10 +54,10 @@ export type Props = {
type State = { type State = {
/** /**
* An object containing the jitsiTrack and the error (if the case) * An list of objects, each containing the microphone label, audio track, device id
* for the microphone that is in use. * and track error if the case.
*/ */
currentMicData: Object audioTracks: Object[]
} }
/** /**
@ -81,10 +82,14 @@ class AudioSettingsContent extends Component<Props, State> {
this._onSpeakerEntryClick = this._onSpeakerEntryClick.bind(this); this._onSpeakerEntryClick = this._onSpeakerEntryClick.bind(this);
this.state = { this.state = {
currentMicData: { audioTracks: props.microphoneDevices.map(({ deviceId, label }) => {
error: false, return {
jitsiTrack: null deviceId,
} hasError: false,
jitsiTrack: null,
label
};
})
}; };
} }
@ -115,20 +120,13 @@ class AudioSettingsContent extends Component<Props, State> {
/** /**
* Renders a single microphone entry. * Renders a single microphone entry.
* *
* @param {Object} data - An object with the deviceId and label of the microphone. * @param {Object} data - An object with the deviceId, jitsiTrack & label of the microphone.
* @param {number} index - The index of the element, used for creating a key. * @param {number} index - The index of the element, used for creating a key.
* @returns {React$Node} * @returns {React$Node}
*/ */
_renderMicrophoneEntry(data, index) { _renderMicrophoneEntry(data, index) {
const { deviceId, label } = data; const { deviceId, label, jitsiTrack, hasError } = data;
const key = `me-${index}`;
const isSelected = deviceId === this.props.currentMicDeviceId; const isSelected = deviceId === this.props.currentMicDeviceId;
let jitsiTrack = null;
let hasError = false;
if (isSelected) {
({ jitsiTrack, hasError } = this.state.currentMicData);
}
return ( return (
<MicrophoneEntry <MicrophoneEntry
@ -136,7 +134,7 @@ class AudioSettingsContent extends Component<Props, State> {
hasError = { hasError } hasError = { hasError }
isSelected = { isSelected } isSelected = { isSelected }
jitsiTrack = { jitsiTrack } jitsiTrack = { jitsiTrack }
key = { key } key = { `me-${index}` }
onClick = { this._onMicrophoneEntryClick }> onClick = { this._onMicrophoneEntryClick }>
{label} {label}
</MicrophoneEntry> </MicrophoneEntry>
@ -166,50 +164,36 @@ class AudioSettingsContent extends Component<Props, State> {
} }
/** /**
* Disposes the audio track for a given micData object. * Creates and updates the audio tracks.
*
* @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} * @returns {void}
*/ */
async _updateCurrentMicData() { async _setTracks() {
await this._disposeTrack(this.state.currentMicData); this._disposeTracks(this.state.audioTracks);
const currentMicData = await createLocalAudioTrack( const audioTracks = await createLocalAudioTracks(
this.props.currentMicDeviceId, this.props.microphoneDevices
); );
// In case the component gets unmounted before the track is created
// avoid a leak by not setting the state
if (this._componentWasUnmounted) { if (this._componentWasUnmounted) {
this._disposeTrack(currentMicData); this._disposeTracks(audioTracks);
} else { } else {
this.setState({ this.setState({
currentMicData audioTracks
}); });
} }
} }
/** /**
* Implements React's {@link Component#componentDidUpdate}. * Disposes the audio tracks.
* *
* @inheritdoc * @param {Object} audioTracks - The object holding the audio tracks.
* @returns {void}
*/ */
componentDidUpdate(prevProps) { _disposeTracks(audioTracks) {
if (prevProps.currentMicDeviceId !== this.props.currentMicDeviceId) { audioTracks.forEach(({ jitsiTrack }) => {
this._updateCurrentMicData(); jitsiTrack && jitsiTrack.dispose();
} });
} }
/** /**
@ -218,7 +202,7 @@ class AudioSettingsContent extends Component<Props, State> {
* @inheritdoc * @inheritdoc
*/ */
componentDidMount() { componentDidMount() {
this._updateCurrentMicData(); this._setTracks();
} }
/** /**
@ -228,16 +212,28 @@ class AudioSettingsContent extends Component<Props, State> {
*/ */
componentWillUnmount() { componentWillUnmount() {
this._componentWasUnmounted = true; this._componentWasUnmounted = true;
this._disposeTrack(this.state.currentMicData); this._disposeTracks(this.state.audioTracks);
} }
/**
* Implements React's {@link Component#componentDidUpdate}.
*
* @inheritdoc
*/
componentDidUpdate(prevProps) {
if (!equals(this.props.microphoneDevices, prevProps.microphoneDevices)) {
this._setTracks();
}
}
/** /**
* Implements React's {@link Component#render}. * Implements React's {@link Component#render}.
* *
* @inheritdoc * @inheritdoc
*/ */
render() { render() {
const { microphoneDevices, outputDevices, t } = this.props; const { outputDevices, t } = this.props;
return ( return (
<div> <div>
@ -245,7 +241,7 @@ class AudioSettingsContent extends Component<Props, State> {
<AudioSettingsHeader <AudioSettingsHeader
IconComponent = { IconMicrophoneEmpty } IconComponent = { IconMicrophoneEmpty }
text = { t('settings.microphones') } /> text = { t('settings.microphones') } />
{microphoneDevices.map((data, i) => {this.state.audioTracks.map((data, i) =>
this._renderMicrophoneEntry(data, i), this._renderMicrophoneEntry(data, i),
)} )}
<AudioSettingsHeader <AudioSettingsHeader

View File

@ -176,27 +176,36 @@ export function createLocalVideoTracks(ids: string[]) {
/** /**
* Returns a promise which resolves with an object containing the corresponding * Returns a promise which resolves with a list of objects containing
* the audio jitsiTrack/error. * the audio track and the corresponding audio device information.
* *
* @param {string} deviceId - The deviceId for the current microphone. * @param {Object[]} devices - A list of microphone devices.
* * @returns {Promise<{
* @returns {Promise<Object>} * deviceId: string,
* hasError: boolean,
* jitsiTrack: Object,
* label: string
* }[]>}
*/ */
export function createLocalAudioTrack(deviceId: string) { export function createLocalAudioTracks(devices: Object[]) {
return createLocalTrack('audio', deviceId) return Promise.all(
.then(jitsiTrack => { devices.map(async ({ deviceId, label }) => {
return { let jitsiTrack = null;
hasError: false, let hasError = false;
jitsiTrack
}; try {
}) jitsiTrack = await createLocalTrack('audio', deviceId);
.catch(() => { } catch (err) {
return { hasError = true;
hasError: true, }
jitsiTrack: null
}; return {
}); deviceId,
hasError,
jitsiTrack,
label
};
}));
} }
/** /**