diff --git a/app/main.ts b/app/main.ts
index 47881df1d..853d39bcd 100644
--- a/app/main.ts
+++ b/app/main.ts
@@ -1862,8 +1862,15 @@ ipc.on(
// Permissions Popup-related IPC calls
-ipc.on('show-permissions-popup', () => {
- showPermissionsPopupWindow(false, false);
+ipc.handle('show-permissions-popup', async () => {
+ try {
+ await showPermissionsPopupWindow(false, false);
+ } catch (error) {
+ getLogger().error(
+ 'show-permissions-popup error:',
+ error && error.stack ? error.stack : error
+ );
+ }
});
ipc.handle(
'show-calling-permissions-popup',
diff --git a/preload.js b/preload.js
index c3a1bf1f6..27ecc86fb 100644
--- a/preload.js
+++ b/preload.js
@@ -239,7 +239,7 @@ try {
// Settings-related events
window.showSettings = () => ipc.send('show-settings');
- window.showPermissionsPopup = () => ipc.send('show-permissions-popup');
+ window.showPermissionsPopup = () => ipc.invoke('show-permissions-popup');
window.showCallingPermissionsPopup = forCamera =>
ipc.invoke('show-calling-permissions-popup', forCamera);
diff --git a/ts/components/conversation/AudioCapture.tsx b/ts/components/conversation/AudioCapture.tsx
index 03cf3db07..31861f8f4 100644
--- a/ts/components/conversation/AudioCapture.tsx
+++ b/ts/components/conversation/AudioCapture.tsx
@@ -97,12 +97,19 @@ export const AudioCapture = ({
const startRecordingShortcut = useStartRecordingShortcut(startRecording);
useKeyboardShortcuts(startRecordingShortcut);
+ const closeToast = useCallback(() => {
+ setToastType(undefined);
+ }, []);
+
// Update timestamp regularly, then timeout if recording goes over five minutes
useEffect(() => {
if (!isRecording) {
return;
}
+ setDurationText(START_DURATION_TEXT);
+ setToastType(ToastType.VoiceNoteLimit);
+
const startTime = Date.now();
const interval = setInterval(() => {
const duration = moment.duration(Date.now() - startTime, 'ms');
@@ -120,8 +127,15 @@ export const AudioCapture = ({
return () => {
clearInterval(interval);
+ closeToast();
};
- }, [completeRecording, errorRecording, isRecording, setDurationText]);
+ }, [
+ closeToast,
+ completeRecording,
+ errorRecording,
+ isRecording,
+ setDurationText,
+ ]);
const clickCancel = useCallback(() => {
cancelRecording();
@@ -131,10 +145,6 @@ export const AudioCapture = ({
completeRecording(conversationId, onSendAudioRecording);
}, [conversationId, completeRecording, onSendAudioRecording]);
- const closeToast = useCallback(() => {
- setToastType(undefined);
- }, []);
-
let toastElement: JSX.Element | undefined;
if (toastType === ToastType.VoiceNoteLimit) {
toastElement = ;
@@ -226,8 +236,6 @@ export const AudioCapture = ({
if (draftAttachments.length) {
setToastType(ToastType.VoiceNoteMustBeOnlyAttachment);
} else {
- setDurationText(START_DURATION_TEXT);
- setToastType(ToastType.VoiceNoteLimit);
startRecording();
}
}}
diff --git a/ts/services/audioRecorder.ts b/ts/services/audioRecorder.ts
index 491798c0a..3a8c70dc5 100644
--- a/ts/services/audioRecorder.ts
+++ b/ts/services/audioRecorder.ts
@@ -1,6 +1,7 @@
// Copyright 2016-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
+import { requestMicrophonePermissions } from '../util/requestMicrophonePermissions';
import * as log from '../logging/log';
import type { WebAudioRecorderClass } from '../window.d';
@@ -42,7 +43,15 @@ export class RecorderClass {
}
}
- async start(): Promise {
+ async start(): Promise {
+ const hasMicrophonePermission = await requestMicrophonePermissions();
+ if (!hasMicrophonePermission) {
+ log.info(
+ 'Recorder/start: Microphone permission was denied, new audio recording not allowed.'
+ );
+ return false;
+ }
+
this.clear();
this.context = new AudioContext();
@@ -61,11 +70,11 @@ export class RecorderClass {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
if (!this.context || !this.input) {
- this.onError(
- this.recorder,
- new Error('Recorder/getUserMedia/stream: Missing context or input!')
+ const err = new Error(
+ 'Recorder/getUserMedia/stream: Missing context or input!'
);
- return;
+ this.onError(this.recorder, err);
+ throw err;
}
this.source = this.context.createMediaStreamSource(stream);
this.source.connect(this.input);
@@ -81,7 +90,10 @@ export class RecorderClass {
if (this.recorder) {
this.recorder.startRecording();
+ return true;
}
+
+ return false;
}
async stop(): Promise {
@@ -120,15 +132,7 @@ export class RecorderClass {
this.clear();
- if (error && error.name === 'NotAllowedError') {
- log.warn('Recorder/onError: Microphone permission missing');
- window.showPermissionsPopup();
- } else {
- log.error(
- 'Recorder/onError:',
- error && error.stack ? error.stack : error
- );
- }
+ log.error('Recorder/onError:', error && error.stack ? error.stack : error);
}
getBlob(): Blob {
diff --git a/ts/services/calling.ts b/ts/services/calling.ts
index f2432ed91..6048900fc 100644
--- a/ts/services/calling.ts
+++ b/ts/services/calling.ts
@@ -92,6 +92,7 @@ import {
} from '../calling/constants';
import { callingMessageToProto } from '../util/callingMessageToProto';
import { getSendOptions } from '../util/getSendOptions';
+import { requestMicrophonePermissions } from '../util/requestMicrophonePermissions';
import { SignalService as Proto } from '../protobuf';
import dataInterface from '../sql/Client';
import {
@@ -1510,20 +1511,8 @@ export class CallingClass {
return true;
}
- private async requestMicrophonePermissions(): Promise {
- const microphonePermission = await window.getMediaPermissions();
- if (!microphonePermission) {
- await window.showCallingPermissionsPopup(false);
-
- // Check the setting again (from the source of truth).
- return window.getMediaPermissions();
- }
-
- return true;
- }
-
private async requestPermissions(isVideoCall: boolean): Promise {
- const microphonePermission = await this.requestMicrophonePermissions();
+ const microphonePermission = await requestMicrophonePermissions();
if (microphonePermission) {
if (isVideoCall) {
return this.requestCameraPermissions();
diff --git a/ts/state/ducks/audioRecorder.ts b/ts/state/ducks/audioRecorder.ts
index 8c8c47e32..c1fa00f4d 100644
--- a/ts/state/ducks/audioRecorder.ts
+++ b/ts/state/ducks/audioRecorder.ts
@@ -77,8 +77,10 @@ function startRecording(): ThunkAction<
return;
}
+ let recordingStarted = false;
+
try {
- await recorder.start();
+ recordingStarted = await recorder.start();
} catch (err) {
dispatch({
type: ERROR_RECORDING,
@@ -87,10 +89,12 @@ function startRecording(): ThunkAction<
return;
}
- dispatch({
- type: START_RECORDING,
- payload: undefined,
- });
+ if (recordingStarted) {
+ dispatch({
+ type: START_RECORDING,
+ payload: undefined,
+ });
+ }
};
}
diff --git a/ts/util/requestMicrophonePermissions.ts b/ts/util/requestMicrophonePermissions.ts
new file mode 100644
index 000000000..ce74bee60
--- /dev/null
+++ b/ts/util/requestMicrophonePermissions.ts
@@ -0,0 +1,14 @@
+// Copyright 2021 Signal Messenger, LLC
+// SPDX-License-Identifier: AGPL-3.0-only
+
+export async function requestMicrophonePermissions(): Promise {
+ const microphonePermission = await window.getMediaPermissions();
+ if (!microphonePermission) {
+ await window.showCallingPermissionsPopup(false);
+
+ // Check the setting again (from the source of truth).
+ return window.getMediaPermissions();
+ }
+
+ return true;
+}
diff --git a/ts/window.d.ts b/ts/window.d.ts
index 93f3672fb..facf4ddb5 100644
--- a/ts/window.d.ts
+++ b/ts/window.d.ts
@@ -160,7 +160,7 @@ declare global {
QRCode: any;
removeSetupMenuItems: () => unknown;
- showPermissionsPopup: () => unknown;
+ showPermissionsPopup: () => Promise;
FontFace: typeof FontFace;
_: typeof Underscore;