don't show error dialog when user has no webcam

instead, retry with just audio.
Also when mounted, check if the user has given enough permissions
to return non-empty labels for the devices, something both ff & chrome
do if you haven't going through the permissions popup yet. If not,
show the permissions button.
pull/21833/head
Bruno Windels 2019-06-26 18:54:15 +02:00
parent b673742e40
commit f6e0cd9a03
3 changed files with 48 additions and 37 deletions

View File

@ -18,6 +18,11 @@ import * as Matrix from 'matrix-js-sdk';
import SettingsStore, {SettingLevel} from "./settings/SettingsStore";
export default {
hasAnyLabeledDevices: async function() {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.some(d => !!d.label);
},
getDevices: function() {
// Only needed for Electron atm, though should work in modern browsers
// once permission has been granted to the webapp

View File

@ -29,48 +29,64 @@ export default class VoiceUserSettingsTab extends React.Component {
super();
this.state = {
mediaDevices: null,
mediaDevices: false,
activeAudioOutput: null,
activeAudioInput: null,
activeVideoInput: null,
};
}
componentWillMount(): void {
this._refreshMediaDevices();
async componentDidMount() {
const canSeeDeviceLabels = await CallMediaHandler.hasAnyLabeledDevices();
if (canSeeDeviceLabels) {
this._refreshMediaDevices();
}
}
_refreshMediaDevices = async (stream) => {
if (stream) {
// kill stream so that we don't leave it lingering around with webcam enabled etc
// as here we called gUM to ask user for permission to their device names only
stream.getTracks().forEach((track) => track.stop());
}
this.setState({
mediaDevices: await CallMediaHandler.getDevices(),
activeAudioOutput: CallMediaHandler.getAudioOutput(),
activeAudioInput: CallMediaHandler.getAudioInput(),
activeVideoInput: CallMediaHandler.getVideoInput(),
});
if (stream) {
// kill stream (after we've enumerated the devices, otherwise we'd get empty labels again)
// so that we don't leave it lingering around with webcam enabled etc
// as here we called gUM to ask user for permission to their device names only
stream.getTracks().forEach((track) => track.stop());
}
};
_requestMediaPermissions = () => {
const getUserMedia = (
window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia
);
if (getUserMedia) {
return getUserMedia.apply(window.navigator, [
{ video: true, audio: true },
this._refreshMediaDevices,
function() {
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
Modal.createTrackedDialog('No media permissions', '', ErrorDialog, {
title: _t('No media permissions'),
description: _t('You may need to manually permit Riot to access your microphone/webcam'),
});
},
]);
_requestMediaPermissions = async () => {
let constraints;
let stream;
let error;
try {
constraints = {video: true, audio: true};
stream = await navigator.mediaDevices.getUserMedia(constraints);
} catch (err) {
// user likely doesn't have a webcam,
// we should still allow to select a microphone
if (err.name === "NotFoundError") {
constraints = { audio: true };
try {
stream = await navigator.mediaDevices.getUserMedia(constraints);
} catch (err) {
error = err;
}
} else {
error = err;
}
}
if (error) {
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
Modal.createTrackedDialog('No media permissions', '', ErrorDialog, {
title: _t('No media permissions'),
description: _t('You may need to manually permit Riot to access your microphone/webcam'),
});
} else {
this._refreshMediaDevices(stream);
}
};
@ -101,15 +117,7 @@ export default class VoiceUserSettingsTab extends React.Component {
_renderDeviceOptions(devices, category) {
return devices.map((d) => {
let label = d.label;
if (!label) {
switch (d.kind) {
case "audioinput": label = _t("Unnamed microphone"); break;
case "audiooutput": label = _t("Unnamed audio output"); break;
case "videoinput": label = _t("Unnamed camera"); break;
}
}
return (<option key={`${category}-${d.deviceId}`} value={d.deviceId}>{label}</option>);
return (<option key={`${category}-${d.deviceId}`} value={d.deviceId}>{d.label}</option>);
});
}
@ -120,6 +128,7 @@ export default class VoiceUserSettingsTab extends React.Component {
let speakerDropdown = null;
let microphoneDropdown = null;
let webcamDropdown = null;
console.log({mediaDevices: this.state.mediaDevices});
if (this.state.mediaDevices === false) {
requestButton = (
<div className='mx_VoiceUserSettingsTab_missingMediaPermissions'>

View File

@ -615,9 +615,6 @@
"Learn more about how we use analytics.": "Learn more about how we use analytics.",
"No media permissions": "No media permissions",
"You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam",
"Unnamed microphone": "Unnamed microphone",
"Unnamed audio output": "Unnamed audio output",
"Unnamed camera": "Unnamed camera",
"Missing media permissions, click the button below to request.": "Missing media permissions, click the button below to request.",
"Request media permissions": "Request media permissions",
"No Audio Outputs detected": "No Audio Outputs detected",