diff --git a/conference.js b/conference.js index 09d33d011..95de0ae20 100644 --- a/conference.js +++ b/conference.js @@ -399,12 +399,12 @@ export default { // If both requests for 'audio' + 'video' and 'audio' only // failed, we assume that there is some problems with user's // microphone and show corresponding dialog. - APP.UI.showDeviceErrorDialog('microphone', audioOnlyError); + APP.UI.showDeviceErrorDialog(audioOnlyError, null); } else { // If request for 'audio' + 'video' failed, but request for // 'audio' only was OK, we assume that we had problems with // camera and show corresponding dialog. - APP.UI.showDeviceErrorDialog('camera', audioAndVideoError); + APP.UI.showDeviceErrorDialog(null, audioAndVideoError); } } @@ -563,56 +563,78 @@ export default { } function createNewTracks(type, cameraDeviceId, micDeviceId) { - let audioOnlyError, videoOnlyError; + let audioTrackCreationError; + let videoTrackCreationError; + let audioRequested = type.indexOf('audio') !== -1; + let videoRequested = type.indexOf('video') !== -1; + let promise; - return createLocalTracks(type, cameraDeviceId, micDeviceId) - .then(onTracksCreated) - .catch((err) => { - // if we tried to create both audio and video tracks - // at once and failed, let's try again only with - // audio. Such situation may happen in case if we - // granted access only to microphone, but not to - // camera. - if (type.indexOf('audio') !== -1 - && type.indexOf('video') !== -1) { - return createLocalTracks(['audio'], null, - micDeviceId); - } else if (type.indexOf('audio') !== -1) { - audioOnlyError = err; - } else if (type.indexOf('video') !== -1) { - videoOnlyError = err; - } + if (audioRequested && micDeviceId !== null) { + if (videoRequested && cameraDeviceId !== null) { + promise = createLocalTracks( + type, cameraDeviceId, micDeviceId) + .catch(() => { + return Promise.all([ + createAudioTrack(false), + createVideoTrack(false)]); + }) + .then((audioTracks, videoTracks) => { + if (audioTrackCreationError) { + if (videoTrackCreationError) { + APP.UI.showDeviceErrorDialog( + audioTrackCreationError, + videoTrackCreationError); + } else { + APP.UI.showDeviceErrorDialog( + audioTrackCreationError, + null); + } + } else if (videoTrackCreationError) { + APP.UI.showDeviceErrorDialog( + null, + videoTrackCreationError); + } - }) - .then(onTracksCreated) - .catch((err) => { - // if we tried to create both audio and video tracks - // at once and failed, let's try again only with - // video. Such situation may happen in case if we - // granted access only to camera, but not to - // microphone. - audioOnlyError = err; - if (type.indexOf('audio') !== -1 - && type.indexOf('video') !== -1) { - return createLocalTracks(['video'], - cameraDeviceId, - null); - } - }) - .then(onTracksCreated) - .catch((err) => { - videoOnlyError = err; + return audioTracks.concat(videoTracks); + }); + } else { + promise = createAudioTrack(); + } + } else if (videoRequested && cameraDeviceId !== null) { + promise = createVideoTrack(); + } else { + promise = Promise.resolve([]); + } - if (videoOnlyError) { - APP.UI.showDeviceErrorDialog( - 'camera', videoOnlyError); - } + return promise + .then(onTracksCreated); - if (audioOnlyError) { - APP.UI.showDeviceErrorDialog( - 'microphone', audioOnlyError); - } - }); + function createAudioTrack(showError) { + return createLocalTracks(['audio'], null, micDeviceId) + .catch(err => { + audioTrackCreationError = err; + + if (showError) { + APP.UI.showDeviceErrorDialog(err, null); + } + + return []; + }); + } + + function createVideoTrack(showError) { + return createLocalTracks( + ['video'], cameraDeviceId, null) + .catch(err => { + videoTrackCreationError = err; + + if (showError) { + APP.UI.showDeviceErrorDialog(null, err); + } + + return []; + }); + } } function onTracksCreated(tracks) { @@ -1034,15 +1056,20 @@ export default { // TrackErrors.CHROME_EXTENSION_INSTALLATION_ERROR // TrackErrors.GENERAL // and any other - let dialogTxt = APP.translation.generateTranslationHTML( - err.name === TrackErrors.PERMISSION_DENIED - ? "dialog.screenSharingPermissionDeniedError" - : "dialog.failtoinstall"); + let dialogTxt; + let dialogTitle; - let dialogTitle = APP.translation.generateTranslationHTML( - err.name === TrackErrors.PERMISSION_DENIED - ? "dialog.permissionDenied" - : "dialog.error"); + if (err.name === TrackErrors.PERMISSION_DENIED) { + dialogTxt = APP.translation.generateTranslationHTML( + "dialog.screenSharingPermissionDeniedError"); + dialogTitle = APP.translation.generateTranslationHTML( + "dialog.error"); + } else { + dialogTxt = APP.translation.generateTranslationHTML( + "dialog.failtoinstall"); + dialogTitle = APP.translation.generateTranslationHTML( + "dialog.permissionDenied"); + } APP.UI.messageHandler.openDialog(dialogTitle, dialogTxt, false); }); @@ -1404,7 +1431,7 @@ export default { APP.settings.setCameraDeviceId(cameraDeviceId); }) .catch((err) => { - APP.UI.showDeviceErrorDialog('camera', err); + APP.UI.showDeviceErrorDialog(null, err); APP.UI.setSelectedCameraFromSettings(); }); } @@ -1420,7 +1447,7 @@ export default { APP.settings.setMicDeviceId(micDeviceId); }) .catch((err) => { - APP.UI.showDeviceErrorDialog('microphone', err); + APP.UI.showDeviceErrorDialog(err, null); APP.UI.setSelectedMicFromSettings(); }); } diff --git a/lang/main.json b/lang/main.json index 0007c544f..2cc4ab304 100644 --- a/lang/main.json +++ b/lang/main.json @@ -226,6 +226,8 @@ "doNotShowWarningAgain": "Don't show this warning again", "permissionDenied": "Permission Denied", "screenSharingPermissionDeniedError": "You have not granted permission to share your screen.", + "micErrorPresent": "There was an error connecting to your microphone.", + "cameraErrorPresent": "There was an error connecting to your camera.", "cameraUnsupportedResolutionError": "Your camera does not support required video resolution.", "cameraUnknownError": "Cannot use camera for a unknown reason.", "cameraPermissionDeniedError": "You have not granted permission to use your camera.", diff --git a/modules/UI/UI.js b/modules/UI/UI.js index 40b12180d..8b50f3041 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -1155,41 +1155,93 @@ UI.showExtensionRequiredDialog = function (url) { }; /** - * Shows dialog with information about camera or microphone error. - * @param {'microphone'|'camera'} type - * @param {JitsiTrackError} error + * Shows dialog with combined information about camera and microphone errors. + * @param {JitsiTrackError} micError + * @param {JitsiTrackError} cameraError */ -UI.showDeviceErrorDialog = function (type, error) { - if (type !== "microphone" && type !== "camera") { - throw new Error("Invalid device type"); +UI.showDeviceErrorDialog = function (micError, cameraError) { + let localStoragePropName = "doNotShowErrorAgain"; + let isMicJitsiTrackErrorAndHasName = micError && micError.name && + micError instanceof JitsiMeetJS.JitsiTrackError; + let isCameraJitsiTrackErrorAndHasName = cameraError && cameraError.name && + cameraError instanceof JitsiMeetJS.JitsiTrackError; + let showDoNotShowWarning = false; + + if (micError && cameraError && isMicJitsiTrackErrorAndHasName && + isCameraJitsiTrackErrorAndHasName) { + showDoNotShowWarning = true; + } else if (micError && isMicJitsiTrackErrorAndHasName && !cameraError) { + showDoNotShowWarning = true; + } else if (cameraError && isCameraJitsiTrackErrorAndHasName && !micError) { + showDoNotShowWarning = true; } - if (error.name && error instanceof JitsiMeetJS.JitsiTrackError && - window.localStorage[type + "DoNotShowErrorAgain-" + error.name] - === "true") { - return; + if (micError) { + localStoragePropName += "-mic-" + micError.name; } - let titleKey = error.name === TrackErrors.PERMISSION_DENIED - ? "dialog.permissionDenied" - : "dialog.error", - errorMsg = JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP[type][error.name] || - JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP[type][TrackErrors.GENERAL], - title = ``, - message = "
" + - (!JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP[type][error.name] - && error.message - ? "