diff --git a/enjoy/src/main/window.ts b/enjoy/src/main/window.ts index 804e8170..939af880 100644 --- a/enjoy/src/main/window.ts +++ b/enjoy/src/main/window.ts @@ -6,6 +6,7 @@ import { ipcMain, shell, dialog, + systemPreferences, } from "electron"; import path from "path"; import db from "@main/db"; @@ -265,6 +266,25 @@ main.init = () => { mainWindow.webContents.openDevTools(); }); + ipcMain.handle( + "system-preferences-media-access", + async (_event, mediaType: "microphone" | "camera") => { + if (process.platform === "linux") return true; + if (process.platform === "win32") + return systemPreferences.getMediaAccessStatus(mediaType) === "granted"; + + if (process.platform === "darwin") { + const status = systemPreferences.getMediaAccessStatus(mediaType); + if (status !== "granted") { + const result = await systemPreferences.askForMediaAccess(mediaType); + return result; + } else { + return true; + } + } + } + ); + // Shell ipcMain.handle("shell-open-external", (_event, url) => { shell.openExternal(url); diff --git a/enjoy/src/preload.ts b/enjoy/src/preload.ts index 14b130ec..29a90b50 100644 --- a/enjoy/src/preload.ts +++ b/enjoy/src/preload.ts @@ -31,6 +31,13 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", { }, version, }, + system: { + preferences: { + mediaAccess: (mediaType: "microphone" | "camera") => { + return ipcRenderer.invoke("system-preferences-media-access", mediaType); + }, + }, + }, providers: { audible: { categories: () => { diff --git a/enjoy/src/renderer/components/record-button.tsx b/enjoy/src/renderer/components/record-button.tsx index 7777e1b6..474f7380 100644 --- a/enjoy/src/renderer/components/record-button.tsx +++ b/enjoy/src/renderer/components/record-button.tsx @@ -1,6 +1,7 @@ import { t } from "i18next"; -import { MicIcon } from "lucide-react"; -import { useState, useEffect, useRef } from "react"; +import { MicIcon, LockIcon } from "lucide-react"; +import { useState, useEffect, useRef, useContext } from "react"; +import { AppSettingsProviderContext } from "@renderer/context"; import RecordPlugin from "wavesurfer.js/dist/plugins/record"; import WaveSurfer from "wavesurfer.js"; import { cn } from "@renderer/lib/utils"; @@ -16,6 +17,19 @@ export const RecordButton = (props: { const { className, disabled, onRecordBegin, onRecordEnd } = props; const [isRecording, setIsRecording] = useState(false); const [duration, setDuration] = useState(0); + const { EnjoyApp } = useContext(AppSettingsProviderContext); + const [access, setAccess] = useState(false); + + const askForMediaAccess = () => { + EnjoyApp.system.preferences.mediaAccess("microphone").then((access) => { + if (access) { + setAccess(true); + } else { + setAccess(false); + toast.warning(t("noMicrophoneAccess")); + } + }); + }; useHotkeys(["command+alt+r", "control+alt+r"], () => { if (disabled) return; @@ -41,6 +55,10 @@ export const RecordButton = (props: { }; }, [isRecording]); + useEffect(() => { + askForMediaAccess(); + }, []); + return (
{ if (disabled) return; - setIsRecording((isRecording) => !isRecording); + if (access) { + setIsRecording((isRecording) => !isRecording); + } else { + askForMediaAccess(); + } }} > {isRecording ? ( @@ -82,6 +104,9 @@ export const RecordButton = (props: { ) : (
+ {!access && ( + + )}
)}
diff --git a/enjoy/src/types/enjoy-app.d.ts b/enjoy/src/types/enjoy-app.d.ts index 2a463ed7..790b215f 100644 --- a/enjoy/src/types/enjoy-app.d.ts +++ b/enjoy/src/types/enjoy-app.d.ts @@ -10,6 +10,11 @@ type EnjoyAppType = { openDevTools: () => Promise; version: string; }; + system: { + preferences: { + mediaAccess: (mediaType: "microphone") => Promise; + }; + }; providers: { audible: { bestsellers: (params?: {