From 87a57ec7e5c7f180f91681cc45a87a4f2162eee9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 3 May 2021 16:41:39 -0600 Subject: [PATCH] Handle no/blocked microphones in voice messages Fixes https://github.com/vector-im/element-web/issues/17139 --- .../views/rooms/VoiceRecordComposerTile.tsx | 62 ++++++++++++++++--- src/i18n/strings/en_EN.json | 4 ++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index bab95291ba..f7e72000c9 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -27,6 +27,9 @@ import LiveRecordingClock from "../voice_messages/LiveRecordingClock"; import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore"; import {UPDATE_EVENT} from "../../../stores/AsyncStore"; import RecordingPlayback from "../voice_messages/RecordingPlayback"; +import Modal from "../../../Modal"; +import ErrorDialog from "../dialogs/ErrorDialog"; +import CallMediaHandler from "../../../CallMediaHandler"; interface IProps { room: Room; @@ -115,16 +118,59 @@ export default class VoiceRecordComposerTile extends React.PureComponent { - if (ev === RecordingState.EndingSoon) return; // ignore this state: it has no UI purpose here - this.setState({recordingPhase: ev}); - }); + // The "microphone access error" dialogs are used a lot, so let's functionify them + const accessError = () => { + Modal.createTrackedDialog('Microphone Access Error', '', ErrorDialog, { + title: _t("Unable to access your microphone"), + description: <> +

{_t( + "We were unable to access your microphone. Please check your browser settings and try again.", + )}

+ , + }); + }; - this.setState({recorder, recordingPhase: RecordingState.Started}); + // Do a sanity test to ensure we're about to grab a valid microphone reference. Things might + // change between this and recording, but at least we will have tried. + try { + const devices = await CallMediaHandler.getDevices(); + if (!devices?.['audioinput']?.length) { + Modal.createTrackedDialog('No Microphone Error', '', ErrorDialog, { + title: _t("No microphone found"), + description: <> +

{_t( + "We didn't find a microphone on your device. Please check your settings and try again.", + )}

+ , + }); + return; + } + // else we probably have a device that is good enough + } catch (e) { + console.error("Error getting devices: ", e); + accessError(); + return; + } + + try { + const recorder = VoiceRecordingStore.instance.startRecording(); + await recorder.start(); + + // We don't need to remove the listener: the recorder will clean that up for us. + recorder.on(UPDATE_EVENT, (ev: RecordingState) => { + if (ev === RecordingState.EndingSoon) return; // ignore this state: it has no UI purpose here + this.setState({recordingPhase: ev}); + }); + + this.setState({recorder, recordingPhase: RecordingState.Started}); + } catch (e) { + console.error("Error starting recording: ", e); + accessError(); + + // noinspection ES6MissingAwait - if this goes wrong we don't want it to affect the call stack + VoiceRecordingStore.instance.disposeRecording(); + } }; private renderWaveformArea(): ReactNode { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 5863f2a834..e194f93f1b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1649,6 +1649,10 @@ "Invited by %(sender)s": "Invited by %(sender)s", "Jump to first unread message.": "Jump to first unread message.", "Mark all as read": "Mark all as read", + "Unable to access your microphone": "Unable to access your microphone", + "We were unable to access your microphone. Please check your browser settings and try again.": "We were unable to access your microphone. Please check your browser settings and try again.", + "No microphone found": "No microphone found", + "We didn't find a microphone on your device. Please check your settings and try again.": "We didn't find a microphone on your device. Please check your settings and try again.", "Record a voice message": "Record a voice message", "Stop the recording": "Stop the recording", "Delete recording": "Delete recording",