mirror of https://github.com/vector-im/riot-web
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
parent
b673742e40
commit
f6e0cd9a03
|
@ -18,6 +18,11 @@ import * as Matrix from 'matrix-js-sdk';
|
||||||
import SettingsStore, {SettingLevel} from "./settings/SettingsStore";
|
import SettingsStore, {SettingLevel} from "./settings/SettingsStore";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
hasAnyLabeledDevices: async function() {
|
||||||
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
return devices.some(d => !!d.label);
|
||||||
|
},
|
||||||
|
|
||||||
getDevices: function() {
|
getDevices: function() {
|
||||||
// Only needed for Electron atm, though should work in modern browsers
|
// Only needed for Electron atm, though should work in modern browsers
|
||||||
// once permission has been granted to the webapp
|
// once permission has been granted to the webapp
|
||||||
|
|
|
@ -29,48 +29,64 @@ export default class VoiceUserSettingsTab extends React.Component {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
mediaDevices: null,
|
mediaDevices: false,
|
||||||
activeAudioOutput: null,
|
activeAudioOutput: null,
|
||||||
activeAudioInput: null,
|
activeAudioInput: null,
|
||||||
activeVideoInput: null,
|
activeVideoInput: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount(): void {
|
async componentDidMount() {
|
||||||
|
const canSeeDeviceLabels = await CallMediaHandler.hasAnyLabeledDevices();
|
||||||
|
if (canSeeDeviceLabels) {
|
||||||
this._refreshMediaDevices();
|
this._refreshMediaDevices();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_refreshMediaDevices = async (stream) => {
|
_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({
|
this.setState({
|
||||||
mediaDevices: await CallMediaHandler.getDevices(),
|
mediaDevices: await CallMediaHandler.getDevices(),
|
||||||
activeAudioOutput: CallMediaHandler.getAudioOutput(),
|
activeAudioOutput: CallMediaHandler.getAudioOutput(),
|
||||||
activeAudioInput: CallMediaHandler.getAudioInput(),
|
activeAudioInput: CallMediaHandler.getAudioInput(),
|
||||||
activeVideoInput: CallMediaHandler.getVideoInput(),
|
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 = () => {
|
_requestMediaPermissions = async () => {
|
||||||
const getUserMedia = (
|
let constraints;
|
||||||
window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia
|
let stream;
|
||||||
);
|
let error;
|
||||||
if (getUserMedia) {
|
try {
|
||||||
return getUserMedia.apply(window.navigator, [
|
constraints = {video: true, audio: true};
|
||||||
{ video: true, audio: true },
|
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
this._refreshMediaDevices,
|
} catch (err) {
|
||||||
function() {
|
// 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');
|
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
||||||
Modal.createTrackedDialog('No media permissions', '', ErrorDialog, {
|
Modal.createTrackedDialog('No media permissions', '', ErrorDialog, {
|
||||||
title: _t('No media permissions'),
|
title: _t('No media permissions'),
|
||||||
description: _t('You may need to manually permit Riot to access your microphone/webcam'),
|
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) {
|
_renderDeviceOptions(devices, category) {
|
||||||
return devices.map((d) => {
|
return devices.map((d) => {
|
||||||
let label = d.label;
|
return (<option key={`${category}-${d.deviceId}`} value={d.deviceId}>{d.label}</option>);
|
||||||
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>);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +128,7 @@ export default class VoiceUserSettingsTab extends React.Component {
|
||||||
let speakerDropdown = null;
|
let speakerDropdown = null;
|
||||||
let microphoneDropdown = null;
|
let microphoneDropdown = null;
|
||||||
let webcamDropdown = null;
|
let webcamDropdown = null;
|
||||||
|
console.log({mediaDevices: this.state.mediaDevices});
|
||||||
if (this.state.mediaDevices === false) {
|
if (this.state.mediaDevices === false) {
|
||||||
requestButton = (
|
requestButton = (
|
||||||
<div className='mx_VoiceUserSettingsTab_missingMediaPermissions'>
|
<div className='mx_VoiceUserSettingsTab_missingMediaPermissions'>
|
||||||
|
|
|
@ -615,9 +615,6 @@
|
||||||
"Learn more about how we use analytics.": "Learn more about how we use analytics.",
|
"Learn more about how we use analytics.": "Learn more about how we use analytics.",
|
||||||
"No media permissions": "No media permissions",
|
"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",
|
"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.",
|
"Missing media permissions, click the button below to request.": "Missing media permissions, click the button below to request.",
|
||||||
"Request media permissions": "Request media permissions",
|
"Request media permissions": "Request media permissions",
|
||||||
"No Audio Outputs detected": "No Audio Outputs detected",
|
"No Audio Outputs detected": "No Audio Outputs detected",
|
||||||
|
|
Loading…
Reference in New Issue