From 8c41f0ecc30662c5e2fef72e5685056a8cddce62 Mon Sep 17 00:00:00 2001 From: Mo Date: Fri, 2 Dec 2022 11:05:32 -0600 Subject: [PATCH] fix: fixes issue where Moments camera wouldn't properly start on desktop app (#2084) --- .../app/javascripts/Main/Media/MediaManager.ts | 18 ++++++++++++++++++ .../Main/Media/MediaManagerInterface.ts | 3 +++ .../javascripts/Main/Remote/RemoteBridge.ts | 7 +++++++ .../desktop/app/javascripts/Main/Window.ts | 4 ++++ .../javascripts/Renderer/CrossProcessBridge.ts | 2 ++ .../app/javascripts/Renderer/DesktopDevice.ts | 4 ++++ .../build/entitlements.mac.inherit.plist | 2 ++ packages/desktop/package.json | 3 +++ .../Domain/Device/DesktopWebCommunication.ts | 2 ++ .../Controllers/Moments/MomentsService.ts | 14 ++++++++++++++ 10 files changed, 59 insertions(+) create mode 100644 packages/desktop/app/javascripts/Main/Media/MediaManager.ts create mode 100644 packages/desktop/app/javascripts/Main/Media/MediaManagerInterface.ts diff --git a/packages/desktop/app/javascripts/Main/Media/MediaManager.ts b/packages/desktop/app/javascripts/Main/Media/MediaManager.ts new file mode 100644 index 000000000..32a3a3f5e --- /dev/null +++ b/packages/desktop/app/javascripts/Main/Media/MediaManager.ts @@ -0,0 +1,18 @@ +import { MediaManagerInterface } from './MediaManagerInterface' + +const { systemPreferences } = require('electron') + +export class MediaManager implements MediaManagerInterface { + async askForMediaAccess(type: 'camera' | 'microphone'): Promise { + const permission = systemPreferences.getMediaAccessStatus(type) + + if (permission === 'granted') { + return true + } else if (permission === 'denied') { + return false + } + + const granted = await systemPreferences.askForMediaAccess(type) + return granted + } +} diff --git a/packages/desktop/app/javascripts/Main/Media/MediaManagerInterface.ts b/packages/desktop/app/javascripts/Main/Media/MediaManagerInterface.ts new file mode 100644 index 000000000..b96b832e4 --- /dev/null +++ b/packages/desktop/app/javascripts/Main/Media/MediaManagerInterface.ts @@ -0,0 +1,3 @@ +export interface MediaManagerInterface { + askForMediaAccess(type: 'camera' | 'microphone'): Promise +} diff --git a/packages/desktop/app/javascripts/Main/Remote/RemoteBridge.ts b/packages/desktop/app/javascripts/Main/Remote/RemoteBridge.ts index a563aa472..e26548a9a 100644 --- a/packages/desktop/app/javascripts/Main/Remote/RemoteBridge.ts +++ b/packages/desktop/app/javascripts/Main/Remote/RemoteBridge.ts @@ -19,6 +19,7 @@ import { MenuManagerInterface } from '../Menus/MenuManagerInterface' import { Component, PackageManagerInterface } from '../Packages/PackageManagerInterface' import { SearchManagerInterface } from '../Search/SearchManagerInterface' import { RemoteDataInterface } from './DataInterface' +import { MediaManagerInterface } from '../Media/MediaManagerInterface' /** * Read https://github.com/electron/remote to understand how electron/remote works. @@ -34,6 +35,7 @@ export class RemoteBridge implements CrossProcessBridge { private data: RemoteDataInterface, private menus: MenuManagerInterface, private fileBackups: FileBackupsDevice, + private media: MediaManagerInterface, ) {} get exposableValue(): CrossProcessBridge { @@ -73,6 +75,7 @@ export class RemoteBridge implements CrossProcessBridge { openFileBackup: this.openFileBackup.bind(this), getFileBackupReadToken: this.getFileBackupReadToken.bind(this), readNextChunk: this.readNextChunk.bind(this), + askForMediaAccess: this.askForMediaAccess.bind(this), } } @@ -223,4 +226,8 @@ export class RemoteBridge implements CrossProcessBridge { public openFileBackup(record: FileBackupRecord): Promise { return this.fileBackups.openFileBackup(record) } + + askForMediaAccess(type: 'camera' | 'microphone'): Promise { + return this.media.askForMediaAccess(type) + } } diff --git a/packages/desktop/app/javascripts/Main/Window.ts b/packages/desktop/app/javascripts/Main/Window.ts index ca6fff845..a452fa860 100644 --- a/packages/desktop/app/javascripts/Main/Window.ts +++ b/packages/desktop/app/javascripts/Main/Window.ts @@ -10,6 +10,7 @@ import { createBackupsManager } from './Backups/BackupsManager' import { BackupsManagerInterface } from './Backups/BackupsManagerInterface' import { FilesBackupManager } from './FileBackups/FileBackupsManager' import { Keychain } from './Keychain/Keychain' +import { MediaManager } from './Media/MediaManager' import { MenuManagerInterface } from './Menus/MenuManagerInterface' import { buildContextMenu, createMenuManager } from './Menus/Menus' import { initializePackageManager } from './Packages/PackageManager' @@ -74,6 +75,7 @@ export async function createWindowState({ }, services.menuManager, services.fileBackupsManager, + services.mediaManager, ) const shouldOpenUrl = (url: string) => url.startsWith('http') || url.startsWith('mailto') @@ -203,6 +205,7 @@ async function createWindowServices(window: Electron.BrowserWindow, appState: Ap const updateManager = setupUpdates(window, appState, backupsManager) const trayManager = createTrayManager(window, appState.store) const spellcheckerManager = createSpellcheckerManager(appState.store, window.webContents, appLocale) + const mediaManager = new MediaManager() if (isTesting()) { handleTestMessage(MessageType.SpellCheckerManager, () => spellcheckerManager) @@ -228,6 +231,7 @@ async function createWindowServices(window: Electron.BrowserWindow, appState: Ap packageManager, searchManager, fileBackupsManager, + mediaManager, } } diff --git a/packages/desktop/app/javascripts/Renderer/CrossProcessBridge.ts b/packages/desktop/app/javascripts/Renderer/CrossProcessBridge.ts index f8bd0df4f..d6a59def8 100644 --- a/packages/desktop/app/javascripts/Renderer/CrossProcessBridge.ts +++ b/packages/desktop/app/javascripts/Renderer/CrossProcessBridge.ts @@ -49,4 +49,6 @@ export interface CrossProcessBridge extends FileBackupsDevice { onInitialDataLoad(): void destroyAllData(): void + + askForMediaAccess(type: 'camera' | 'microphone'): Promise } diff --git a/packages/desktop/app/javascripts/Renderer/DesktopDevice.ts b/packages/desktop/app/javascripts/Renderer/DesktopDevice.ts index cb7ab8685..42fbcfc93 100644 --- a/packages/desktop/app/javascripts/Renderer/DesktopDevice.ts +++ b/packages/desktop/app/javascripts/Renderer/DesktopDevice.ts @@ -166,4 +166,8 @@ export class DesktopDevice extends WebOrDesktopDevice implements DesktopDeviceIn isDeviceDestroyed(): boolean { return false } + + askForMediaAccess(type: 'camera' | 'microphone'): Promise { + return this.remoteBridge.askForMediaAccess(type) + } } diff --git a/packages/desktop/build/entitlements.mac.inherit.plist b/packages/desktop/build/entitlements.mac.inherit.plist index 6bc22e913..180ed0a7e 100644 --- a/packages/desktop/build/entitlements.mac.inherit.plist +++ b/packages/desktop/build/entitlements.mac.inherit.plist @@ -10,5 +10,7 @@ com.apple.security.cs.disable-library-validation + com.apple.security.device.camera + diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 6629e49b4..96441907d 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -104,6 +104,9 @@ "hardenedRuntime": true, "entitlements": "./build/entitlements.mac.inherit.plist", "entitlementsInherit": "./build/entitlements.mac.inherit.plist", + "extendInfo": { + "NSCameraUsageDescription": "Standard Notes requires access to your camera to enable the Moments feature." + }, "target": [ "dmg", "zip" diff --git a/packages/services/src/Domain/Device/DesktopWebCommunication.ts b/packages/services/src/Domain/Device/DesktopWebCommunication.ts index cb36dc560..f4502452b 100644 --- a/packages/services/src/Domain/Device/DesktopWebCommunication.ts +++ b/packages/services/src/Domain/Device/DesktopWebCommunication.ts @@ -19,6 +19,8 @@ export interface WebClientRequiresDesktopMethods extends FileBackupsDevice { downloadBackup(): void | Promise get extensionsServerHost(): string + + askForMediaAccess(type: 'camera' | 'microphone'): Promise } export interface DesktopClientRequiresWebMethods { diff --git a/packages/web/src/javascripts/Controllers/Moments/MomentsService.ts b/packages/web/src/javascripts/Controllers/Moments/MomentsService.ts index 3db7b0bac..2fcb0f622 100644 --- a/packages/web/src/javascripts/Controllers/Moments/MomentsService.ts +++ b/packages/web/src/javascripts/Controllers/Moments/MomentsService.ts @@ -86,6 +86,20 @@ export class MomentsService extends AbstractViewController { message: 'Capturing Moment...', }) + if (this.application.desktopDevice) { + const granted = await this.application.desktopDevice.askForMediaAccess('camera') + if (!granted) { + dismissToast(toastId) + addToast({ + type: ToastType.Error, + message: 'Please enable Camera permissions for Standard Notes to enable Moments.', + duration: 3000, + }) + + return + } + } + const { canvas, video, stream, width, height } = await preparePhotoOperation() const filename = `Moment ${dateToStringStyle1(new Date())}.png`