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";
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue