|
@@ -4,7 +4,8 @@ import React, { Component } from 'react';
|
4
|
4
|
|
5
|
5
|
import { translate } from '../../../../base/i18n';
|
6
|
6
|
import { IconMicrophoneEmpty, IconVolumeEmpty } from '../../../../base/icons';
|
7
|
|
-import { createLocalAudioTrack } from '../../../functions';
|
|
7
|
+import { equals } from '../../../../base/redux';
|
|
8
|
+import { createLocalAudioTracks } from '../../../functions';
|
8
|
9
|
|
9
|
10
|
import AudioSettingsHeader from './AudioSettingsHeader';
|
10
|
11
|
import MicrophoneEntry from './MicrophoneEntry';
|
|
@@ -53,10 +54,10 @@ export type Props = {
|
53
|
54
|
type State = {
|
54
|
55
|
|
55
|
56
|
/**
|
56
|
|
- * An object containing the jitsiTrack and the error (if the case)
|
57
|
|
- * for the microphone that is in use.
|
|
57
|
+ * An list of objects, each containing the microphone label, audio track, device id
|
|
58
|
+ * and track error if the case.
|
58
|
59
|
*/
|
59
|
|
- currentMicData: Object
|
|
60
|
+ audioTracks: Object[]
|
60
|
61
|
}
|
61
|
62
|
|
62
|
63
|
/**
|
|
@@ -81,10 +82,14 @@ class AudioSettingsContent extends Component<Props, State> {
|
81
|
82
|
this._onSpeakerEntryClick = this._onSpeakerEntryClick.bind(this);
|
82
|
83
|
|
83
|
84
|
this.state = {
|
84
|
|
- currentMicData: {
|
85
|
|
- error: false,
|
86
|
|
- jitsiTrack: null
|
87
|
|
- }
|
|
85
|
+ audioTracks: props.microphoneDevices.map(({ deviceId, label }) => {
|
|
86
|
+ return {
|
|
87
|
+ deviceId,
|
|
88
|
+ hasError: false,
|
|
89
|
+ jitsiTrack: null,
|
|
90
|
+ label
|
|
91
|
+ };
|
|
92
|
+ })
|
88
|
93
|
};
|
89
|
94
|
}
|
90
|
95
|
|
|
@@ -115,20 +120,13 @@ class AudioSettingsContent extends Component<Props, State> {
|
115
|
120
|
/**
|
116
|
121
|
* Renders a single microphone entry.
|
117
|
122
|
*
|
118
|
|
- * @param {Object} data - An object with the deviceId and label of the microphone.
|
|
123
|
+ * @param {Object} data - An object with the deviceId, jitsiTrack & label of the microphone.
|
119
|
124
|
* @param {number} index - The index of the element, used for creating a key.
|
120
|
125
|
* @returns {React$Node}
|
121
|
126
|
*/
|
122
|
127
|
_renderMicrophoneEntry(data, index) {
|
123
|
|
- const { deviceId, label } = data;
|
124
|
|
- const key = `me-${index}`;
|
|
128
|
+ const { deviceId, label, jitsiTrack, hasError } = data;
|
125
|
129
|
const isSelected = deviceId === this.props.currentMicDeviceId;
|
126
|
|
- let jitsiTrack = null;
|
127
|
|
- let hasError = false;
|
128
|
|
-
|
129
|
|
- if (isSelected) {
|
130
|
|
- ({ jitsiTrack, hasError } = this.state.currentMicData);
|
131
|
|
- }
|
132
|
130
|
|
133
|
131
|
return (
|
134
|
132
|
<MicrophoneEntry
|
|
@@ -136,7 +134,7 @@ class AudioSettingsContent extends Component<Props, State> {
|
136
|
134
|
hasError = { hasError }
|
137
|
135
|
isSelected = { isSelected }
|
138
|
136
|
jitsiTrack = { jitsiTrack }
|
139
|
|
- key = { key }
|
|
137
|
+ key = { `me-${index}` }
|
140
|
138
|
onClick = { this._onMicrophoneEntryClick }>
|
141
|
139
|
{label}
|
142
|
140
|
</MicrophoneEntry>
|
|
@@ -166,50 +164,36 @@ class AudioSettingsContent extends Component<Props, State> {
|
166
|
164
|
}
|
167
|
165
|
|
168
|
166
|
/**
|
169
|
|
- * Disposes the audio track for a given micData object.
|
170
|
|
- *
|
171
|
|
- * @param {Object} micData - The object holding the track.
|
172
|
|
- * @returns {Promise<void>}
|
173
|
|
- */
|
174
|
|
- _disposeTrack(micData) {
|
175
|
|
- const { jitsiTrack } = micData;
|
176
|
|
-
|
177
|
|
- return jitsiTrack ? jitsiTrack.dispose() : Promise.resolve();
|
178
|
|
- }
|
179
|
|
-
|
180
|
|
- /**
|
181
|
|
- * Updates the current microphone data.
|
182
|
|
- * Disposes previously created track and creates a new one.
|
|
167
|
+ * Creates and updates the audio tracks.
|
183
|
168
|
*
|
184
|
169
|
* @returns {void}
|
185
|
170
|
*/
|
186
|
|
- async _updateCurrentMicData() {
|
187
|
|
- await this._disposeTrack(this.state.currentMicData);
|
|
171
|
+ async _setTracks() {
|
|
172
|
+ this._disposeTracks(this.state.audioTracks);
|
188
|
173
|
|
189
|
|
- const currentMicData = await createLocalAudioTrack(
|
190
|
|
- this.props.currentMicDeviceId,
|
|
174
|
+ const audioTracks = await createLocalAudioTracks(
|
|
175
|
+ this.props.microphoneDevices
|
191
|
176
|
);
|
192
|
177
|
|
193
|
|
- // In case the component gets unmounted before the track is created
|
194
|
|
- // avoid a leak by not setting the state
|
195
|
178
|
if (this._componentWasUnmounted) {
|
196
|
|
- this._disposeTrack(currentMicData);
|
|
179
|
+ this._disposeTracks(audioTracks);
|
197
|
180
|
} else {
|
198
|
181
|
this.setState({
|
199
|
|
- currentMicData
|
|
182
|
+ audioTracks
|
200
|
183
|
});
|
201
|
184
|
}
|
202
|
185
|
}
|
203
|
186
|
|
204
|
187
|
/**
|
205
|
|
- * Implements React's {@link Component#componentDidUpdate}.
|
|
188
|
+ * Disposes the audio tracks.
|
206
|
189
|
*
|
207
|
|
- * @inheritdoc
|
|
190
|
+ * @param {Object} audioTracks - The object holding the audio tracks.
|
|
191
|
+ * @returns {void}
|
208
|
192
|
*/
|
209
|
|
- componentDidUpdate(prevProps) {
|
210
|
|
- if (prevProps.currentMicDeviceId !== this.props.currentMicDeviceId) {
|
211
|
|
- this._updateCurrentMicData();
|
212
|
|
- }
|
|
193
|
+ _disposeTracks(audioTracks) {
|
|
194
|
+ audioTracks.forEach(({ jitsiTrack }) => {
|
|
195
|
+ jitsiTrack && jitsiTrack.dispose();
|
|
196
|
+ });
|
213
|
197
|
}
|
214
|
198
|
|
215
|
199
|
/**
|
|
@@ -218,7 +202,7 @@ class AudioSettingsContent extends Component<Props, State> {
|
218
|
202
|
* @inheritdoc
|
219
|
203
|
*/
|
220
|
204
|
componentDidMount() {
|
221
|
|
- this._updateCurrentMicData();
|
|
205
|
+ this._setTracks();
|
222
|
206
|
}
|
223
|
207
|
|
224
|
208
|
/**
|
|
@@ -228,16 +212,28 @@ class AudioSettingsContent extends Component<Props, State> {
|
228
|
212
|
*/
|
229
|
213
|
componentWillUnmount() {
|
230
|
214
|
this._componentWasUnmounted = true;
|
231
|
|
- this._disposeTrack(this.state.currentMicData);
|
|
215
|
+ this._disposeTracks(this.state.audioTracks);
|
|
216
|
+ }
|
|
217
|
+
|
|
218
|
+ /**
|
|
219
|
+ * Implements React's {@link Component#componentDidUpdate}.
|
|
220
|
+ *
|
|
221
|
+ * @inheritdoc
|
|
222
|
+ */
|
|
223
|
+ componentDidUpdate(prevProps) {
|
|
224
|
+ if (!equals(this.props.microphoneDevices, prevProps.microphoneDevices)) {
|
|
225
|
+ this._setTracks();
|
|
226
|
+ }
|
232
|
227
|
}
|
233
|
228
|
|
|
229
|
+
|
234
|
230
|
/**
|
235
|
231
|
* Implements React's {@link Component#render}.
|
236
|
232
|
*
|
237
|
233
|
* @inheritdoc
|
238
|
234
|
*/
|
239
|
235
|
render() {
|
240
|
|
- const { microphoneDevices, outputDevices, t } = this.props;
|
|
236
|
+ const { outputDevices, t } = this.props;
|
241
|
237
|
|
242
|
238
|
return (
|
243
|
239
|
<div>
|
|
@@ -245,7 +241,7 @@ class AudioSettingsContent extends Component<Props, State> {
|
245
|
241
|
<AudioSettingsHeader
|
246
|
242
|
IconComponent = { IconMicrophoneEmpty }
|
247
|
243
|
text = { t('settings.microphones') } />
|
248
|
|
- {microphoneDevices.map((data, i) =>
|
|
244
|
+ {this.state.audioTracks.map((data, i) =>
|
249
|
245
|
this._renderMicrophoneEntry(data, i),
|
250
|
246
|
)}
|
251
|
247
|
<AudioSettingsHeader
|