Adds error dialog when voice recorder cannot start

This commit is contained in:
Josh Perez 2021-09-30 16:13:47 -04:00 committed by GitHub
parent 6614206921
commit 31d1f25b18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 27 deletions

View file

@ -937,6 +937,10 @@
"message": "A voice message must have only one attachment.", "message": "A voice message must have only one attachment.",
"description": "Shown in toast if tries to record a voice note with any staged attachments" "description": "Shown in toast if tries to record a voice note with any staged attachments"
}, },
"voiceNoteError": {
"message": "There was an error with the voice recorder.",
"description": "Shown in a dialog to inform user that we experienced an unrecoverable error"
},
"attachmentSaved": { "attachmentSaved": {
"message": "Attachment saved. Click to show in folder.", "message": "Attachment saved. Click to show in folder.",
"description": "Shown after user selects to save to downloads", "description": "Shown after user selects to save to downloads",

View file

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastVoiceNoteLimit = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('voiceNoteError')}</Toast>;
};

View file

@ -147,16 +147,50 @@ export const AudioCapture = ({
); );
} }
let confirmationDialogText: string | undefined; let confirmationDialog: JSX.Element | undefined;
if (errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.Blur) { if (
confirmationDialogText = i18n('voiceRecordingInterruptedBlur'); errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.Blur ||
} else if (
errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.Timeout errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.Timeout
) { ) {
confirmationDialogText = i18n('voiceRecordingInterruptedMax'); const confirmationDialogText =
errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.Blur
? i18n('voiceRecordingInterruptedBlur')
: i18n('voiceRecordingInterruptedMax');
confirmationDialog = (
<ConfirmationDialog
i18n={i18n}
onCancel={clickCancel}
onClose={noop}
cancelText={i18n('discard')}
actions={[
{
text: i18n('sendAnyway'),
style: 'affirmative',
action: clickSend,
},
]}
>
{confirmationDialogText}
</ConfirmationDialog>
);
} else if (
errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.ErrorRecording
) {
confirmationDialog = (
<ConfirmationDialog
i18n={i18n}
onCancel={clickCancel}
onClose={noop}
cancelText={i18n('ok')}
actions={[]}
>
{i18n('voiceNoteError')}
</ConfirmationDialog>
);
} }
if (isRecording && !confirmationDialogText) { if (isRecording && !confirmationDialog) {
return ( return (
<> <>
<div className="AudioCapture"> <div className="AudioCapture">
@ -203,23 +237,7 @@ export const AudioCapture = ({
title={i18n('voiceRecording--start')} title={i18n('voiceRecording--start')}
type="button" type="button"
/> />
{confirmationDialogText ? ( {confirmationDialog}
<ConfirmationDialog
i18n={i18n}
onCancel={clickCancel}
onClose={noop}
cancelText={i18n('discard')}
actions={[
{
text: i18n('sendAnyway'),
style: 'affirmative',
action: clickSend,
},
]}
>
{confirmationDialogText}
</ConfirmationDialog>
) : null}
</div> </div>
{toastElement} {toastElement}
</> </>

View file

@ -68,8 +68,12 @@ export class RecorderClass {
this.source = this.context.createMediaStreamSource(stream); this.source = this.context.createMediaStreamSource(stream);
this.source.connect(this.input); this.source.connect(this.input);
} catch (err) { } catch (err) {
log.error('Recorder.onGetUserMediaError:', err); log.error(
'Recorder.onGetUserMediaError:',
err && err.stack ? err.stack : err
);
this.clear(); this.clear();
throw err;
} }
if (this.recorder) { if (this.recorder) {

View file

@ -14,6 +14,7 @@ import { useBoundActions } from '../../hooks/useBoundActions';
export enum ErrorDialogAudioRecorderType { export enum ErrorDialogAudioRecorderType {
Blur, Blur,
ErrorRecording,
Timeout, Timeout,
} }
@ -69,14 +70,22 @@ function startRecording(): ThunkAction<
void, void,
RootStateType, RootStateType,
unknown, unknown,
StartRecordingAction StartRecordingAction | ErrorRecordingAction
> { > {
return (dispatch, getState) => { return async (dispatch, getState) => {
if (getState().composer.attachments.length) { if (getState().composer.attachments.length) {
return; return;
} }
recorder.start(); try {
await recorder.start();
} catch (err) {
dispatch({
type: ERROR_RECORDING,
payload: ErrorDialogAudioRecorderType.ErrorRecording,
});
return;
}
dispatch({ dispatch({
type: START_RECORDING, type: START_RECORDING,