diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 373dda5f8f..f71d5c095c 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -488,7 +488,8 @@ "speakerAdd": "Right click to add a speaker.", "speakerChange": "Right click to change speaker.", "speakerSelect": "Select speaker of this audio recording", - "recordingError": "Recording error", + "recordingError": "Recording error.", + "recordingPermission": "Please check if microphone permission is enabled in your browser.", "deleteRecording": "Delete Recording", "audioStreamError": "Error getting audio stream!", "noMicAccess": "No microphone access. Audio recording disabled." diff --git a/src/components/Pronunciations/AudioRecorder.tsx b/src/components/Pronunciations/AudioRecorder.tsx index 2d09cc4f5e..981e949d27 100644 --- a/src/components/Pronunciations/AudioRecorder.tsx +++ b/src/components/Pronunciations/AudioRecorder.tsx @@ -4,6 +4,7 @@ import { toast } from "react-toastify"; import RecorderContext from "components/Pronunciations/RecorderContext"; import RecorderIcon from "components/Pronunciations/RecorderIcon"; +import { isBrowserFirefox } from "components/Pronunciations/utilities"; import { useAppSelector } from "rootRedux/hooks"; import { type StoreState } from "rootRedux/types"; import { FileWithSpeakerId } from "types/word"; @@ -33,7 +34,13 @@ export default function AudioRecorder(props: RecorderProps): ReactElement { // Prevent starting a recording before a previous one is finished. await stopRecording(); - recorder.startRecording(props.id); + if (!recorder.startRecording(props.id)) { + let errorMessage = t("pronunciations.recordingError"); + if (isBrowserFirefox()) { + errorMessage += ` ${t("pronunciations.recordingPermission")}`; + } + toast.error(errorMessage); + } } async function stopRecording(): Promise { diff --git a/src/components/Pronunciations/Recorder.ts b/src/components/Pronunciations/Recorder.ts index 9fb8540073..489b84c68f 100644 --- a/src/components/Pronunciations/Recorder.ts +++ b/src/components/Pronunciations/Recorder.ts @@ -29,10 +29,16 @@ export default class Recorder { : undefined; } - startRecording(id: string): void { - this.recordRTC?.reset(); + /** If recorder exists, start recording and return true. + * If no recorder, return false. */ + startRecording(id: string): boolean { + if (!this.recordRTC) { + return false; + } + this.recordRTC.reset(); this.id = id; - this.recordRTC?.startRecording(); + this.recordRTC.startRecording(); + return true; } stopRecording(): Promise { diff --git a/src/components/Pronunciations/utilities.ts b/src/components/Pronunciations/utilities.ts index eb71348689..9d33642b9a 100644 --- a/src/components/Pronunciations/utilities.ts +++ b/src/components/Pronunciations/utilities.ts @@ -35,16 +35,16 @@ export async function uploadFileFromPronunciation( * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox */ const firefoxBrowsers = ["firefox", "focus", "fxios"]; -/** Check if a user-agent string is of a Firefox browser. */ -function isUserAgentFirefox(userAgent: string): boolean { - const uaLower = userAgent.toLocaleLowerCase(); +/** Check if browser is a Firefox browser. */ +export function isBrowserFirefox(): boolean { + const uaLower = navigator.userAgent.toLocaleLowerCase(); return firefoxBrowsers.some((browser) => uaLower.includes(browser)); } /** Checks if the user has granted mic permission to The Combine, * except on Firefox assumes permission is granted. */ export async function checkMicPermission(): Promise { - if (!isUserAgentFirefox(navigator.userAgent)) { + if (!isBrowserFirefox()) { const result = await navigator.permissions.query({ name: "microphone" as PermissionName, // This causes a TypeError on Firefox. });