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.",
"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": {
"message": "Attachment saved. Click to show in folder.",
"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;
if (errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.Blur) {
confirmationDialogText = i18n('voiceRecordingInterruptedBlur');
} else if (
let confirmationDialog: JSX.Element | undefined;
if (
errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.Blur ||
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 (
<>
<div className="AudioCapture">
@ -203,23 +237,7 @@ export const AudioCapture = ({
title={i18n('voiceRecording--start')}
type="button"
/>
{confirmationDialogText ? (
<ConfirmationDialog
i18n={i18n}
onCancel={clickCancel}
onClose={noop}
cancelText={i18n('discard')}
actions={[
{
text: i18n('sendAnyway'),
style: 'affirmative',
action: clickSend,
},
]}
>
{confirmationDialogText}
</ConfirmationDialog>
) : null}
{confirmationDialog}
</div>
{toastElement}
</>

View File

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

View File

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