diff --git a/packages/frontend/apps/electron/src/main/recording/feature.ts b/packages/frontend/apps/electron/src/main/recording/feature.ts index d5926cd71..649d79801 100644 --- a/packages/frontend/apps/electron/src/main/recording/feature.ts +++ b/packages/frontend/apps/electron/src/main/recording/feature.ts @@ -471,6 +471,21 @@ function setupMediaListeners() { ); } +function askForScreenRecordingPermission() { + if (!isMacOS()) { + return false; + } + try { + const ShareableContent = require('@affine/native').ShareableContent; + // this will trigger the permission prompt + new ShareableContent(); + return true; + } catch (error) { + logger.error('failed to ask for screen recording permission', error); + } + return false; +} + // will be called when the app is ready or when the user has enabled the recording feature in settings export function setupRecordingFeature() { if (!MeetingsSettingsState.value.enabled || !checkCanRecordMeeting()) { @@ -788,10 +803,15 @@ export const checkMeetingPermissions = () => { ) as Record<(typeof mediaTypes)[number], boolean>; }; -export const askForMeetingPermission = async (type: 'microphone') => { +export const askForMeetingPermission = async ( + type: 'microphone' | 'screen' +) => { if (!isMacOS()) { return false; } + if (type === 'screen') { + return askForScreenRecordingPermission(); + } return systemPreferences.askForMediaAccess(type); }; diff --git a/packages/frontend/apps/electron/src/main/recording/index.ts b/packages/frontend/apps/electron/src/main/recording/index.ts index 3a9893525..20dc99de1 100644 --- a/packages/frontend/apps/electron/src/main/recording/index.ts +++ b/packages/frontend/apps/electron/src/main/recording/index.ts @@ -77,18 +77,17 @@ export const recordingHandlers = { checkMeetingPermissions: async () => { return checkMeetingPermissions(); }, - askForMeetingPermission: async (_, type: 'microphone') => { + askForMeetingPermission: async (_, type: 'screen' | 'microphone') => { return askForMeetingPermission(type); }, showRecordingPermissionSetting: async (_, type: 'screen' | 'microphone') => { - const urlMap = { - screen: 'Privacy_ScreenCapture', - microphone: 'Privacy_Microphone', - }; if (isMacOS()) { - return shell.openExternal( - `x-apple.systempreferences:com.apple.preference.security?${urlMap[type]}` - ); + const urlMap = { + screen: 'Privacy_ScreenCapture', + microphone: 'Privacy_Microphone', + }; + const url = `x-apple.systempreferences:com.apple.preference.security?${urlMap[type]}`; + return shell.openExternal(url); } // this only available on MacOS return false; diff --git a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/meetings/index.tsx b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/meetings/index.tsx index bae5bec61..d6ba6fa6a 100644 --- a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/meetings/index.tsx +++ b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/meetings/index.tsx @@ -1,4 +1,5 @@ import { + Button, IconButton, Menu, MenuItem, @@ -11,6 +12,7 @@ import { SettingWrapper, } from '@affine/component/setting-components'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; +import { DesktopApiService } from '@affine/core/modules/desktop-api'; import { MeetingSettingsService } from '@affine/core/modules/media/services/meeting-settings'; import type { MeetingSettingsSchema } from '@affine/electron/main/shared-state-schema'; import { Trans, useI18n } from '@affine/i18n'; @@ -133,6 +135,7 @@ const PermissionSettingRow = ({ const MeetingsSettingsMain = () => { const t = useI18n(); const meetingSettingsService = useService(MeetingSettingsService); + const desktopApiService = useService(DesktopApiService); const settings = useLiveData(meetingSettingsService.settings$); const [recordingFeatureAvailable, setRecordingFeatureAvailable] = useState(false); @@ -175,26 +178,22 @@ const MeetingsSettingsMain = () => { [meetingSettingsService] ); - const handleOpenScreenRecordingPermissionSetting = - useAsyncCallback(async () => { - await meetingSettingsService.showRecordingPermissionSetting('screen'); - }, [meetingSettingsService]); - - const handleOpenMicrophoneRecordingPermissionSetting = - useAsyncCallback(async () => { - const result = - await meetingSettingsService.askForMeetingPermission('microphone'); - if (!result) { - await meetingSettingsService.showRecordingPermissionSetting( - 'microphone' - ); - } - }, [meetingSettingsService]); + const handleOpenPermissionSetting = useAsyncCallback( + async (type: 'screen' | 'microphone') => { + await meetingSettingsService.askForMeetingPermission(type); + await meetingSettingsService.showRecordingPermissionSetting(type); + }, + [meetingSettingsService] + ); const handleOpenSavedRecordings = useAsyncCallback(async () => { await meetingSettingsService.openSavedRecordings(); }, [meetingSettingsService]); + const handleRestartApp = useAsyncCallback(async () => { + await desktopApiService.handler.ui.restartApp(); + }, [desktopApiService]); + return (