From 39f8a920591b80ba9821dec07b66e07ee6855a33 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Wed, 31 Jan 2024 11:28:34 -0300 Subject: [PATCH] feat: audio recording permission handling in MicOrSendButton component (#5515) --- .../components/Buttons/MicOrSendButton.tsx | 45 ++++++++++++++----- .../components/RecordAudio/RecordAudio.tsx | 18 +++++++- app/i18n/locales/en.json | 4 +- app/i18n/locales/pt-BR.json | 2 + app/lib/methods/helpers/openAppSettings.ts | 11 +++++ 5 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 app/lib/methods/helpers/openAppSettings.ts diff --git a/app/containers/MessageComposer/components/Buttons/MicOrSendButton.tsx b/app/containers/MessageComposer/components/Buttons/MicOrSendButton.tsx index d840c16a7..1ce52d0ae 100644 --- a/app/containers/MessageComposer/components/Buttons/MicOrSendButton.tsx +++ b/app/containers/MessageComposer/components/Buttons/MicOrSendButton.tsx @@ -1,14 +1,18 @@ -import React, { useContext } from 'react'; import { Audio } from 'expo-av'; +import React, { useContext } from 'react'; +import { Alert } from 'react-native'; +import { PermissionStatus } from 'expo-camera'; -import { BaseButton } from './BaseButton'; -import { MessageInnerContext, useMessageComposerApi, useMicOrSend } from '../../context'; -import { useTheme } from '../../../../theme'; +import i18n from '../../../../i18n'; import { useAppSelector } from '../../../../lib/hooks'; -import { useCanUploadFile } from '../../hooks'; +import { openAppSettings } from '../../../../lib/methods/helpers/openAppSettings'; +import { useTheme } from '../../../../theme'; import { useRoomContext } from '../../../../views/RoomView/context'; +import { MessageInnerContext, useMessageComposerApi, useMicOrSend } from '../../context'; +import { useCanUploadFile } from '../../hooks'; +import { BaseButton } from './BaseButton'; -export const MicOrSendButton = () => { +export const MicOrSendButton = (): React.ReactElement | null => { const { rid, sharing } = useRoomContext(); const micOrSend = useMicOrSend(); const { sendMessage } = useContext(MessageInnerContext); @@ -17,11 +21,32 @@ export const MicOrSendButton = () => { const { colors } = useTheme(); const { setRecordingAudio } = useMessageComposerApi(); + const requestPermissionAndStartToRecordAudio = () => + Audio.requestPermissionsAsync() + .then(({ granted }) => setRecordingAudio(granted)) + .catch(() => {}); + const startRecording = async () => { - const permission = await Audio.requestPermissionsAsync(); - if (permission.granted) { - setRecordingAudio(true); - } + const { status, granted, canAskAgain } = await Audio.getPermissionsAsync(); + if (granted) return setRecordingAudio(true); + if (status === PermissionStatus.UNDETERMINED) return requestPermissionAndStartToRecordAudio(); + if (canAskAgain) return requestPermissionAndStartToRecordAudio(); + + Alert.alert( + i18n.t('Microphone_access_needed_to_record_audio'), + i18n.t('Go_to_your_device_settings_and_allow_microphone'), + [ + { + text: i18n.t('Cancel'), + style: 'cancel' + }, + { + text: i18n.t('Settings'), + onPress: openAppSettings + } + ], + { cancelable: false } + ); }; if (micOrSend === 'send' || sharing) { diff --git a/app/containers/MessageComposer/components/RecordAudio/RecordAudio.tsx b/app/containers/MessageComposer/components/RecordAudio/RecordAudio.tsx index 09778a739..00e382120 100644 --- a/app/containers/MessageComposer/components/RecordAudio/RecordAudio.tsx +++ b/app/containers/MessageComposer/components/RecordAudio/RecordAudio.tsx @@ -27,6 +27,7 @@ export const RecordAudio = (): ReactElement | null => { const [styles, colors] = useStyle(); const recordingRef = useRef(); const durationRef = useRef({} as IDurationRef); + const numberOfTriesRef = useRef(0); const [status, setStatus] = React.useState<'recording' | 'reviewing'>('recording'); const { setRecordingAudio } = useMessageComposerApi(); const { rid, tmid } = useRoomContext(); @@ -43,8 +44,21 @@ export const RecordAudio = (): ReactElement | null => { await recordingRef.current.prepareToRecordAsync(RECORDING_SETTINGS); recordingRef.current.setOnRecordingStatusUpdate(durationRef.current.onRecordingStatusUpdate); await recordingRef.current.startAsync(); - } catch (error) { - console.error(error); + } catch (error: any) { + // error only occurs on iOS devices + if (error?.code === 'E_AUDIO_RECORDERNOTCREATED') { + if (numberOfTriesRef.current <= 5) { + recordingRef.current = undefined; + numberOfTriesRef.current += 1; + setTimeout(() => { + record(); + }, 100); + } else { + console.error(error); + } + } else { + console.error(error); + } } }; record(); diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json index a9ff444ed..bbc51d27d 100644 --- a/app/i18n/locales/en.json +++ b/app/i18n/locales/en.json @@ -803,5 +803,7 @@ "Inline_code": "Inline code", "Code_block": "Code block", "Add_thread_reply": "Add thread reply", - "Message_roomname": "Message {{roomName}}" + "Message_roomname": "Message {{roomName}}", + "Microphone_access_needed_to_record_audio": "Microphone access needed to record audio", + "Go_to_your_device_settings_and_allow_microphone": "Go to your device settings and allow microphone access for Rocket.Chat" } \ No newline at end of file diff --git a/app/i18n/locales/pt-BR.json b/app/i18n/locales/pt-BR.json index cb4f4d7d2..537e95123 100644 --- a/app/i18n/locales/pt-BR.json +++ b/app/i18n/locales/pt-BR.json @@ -794,6 +794,8 @@ "You_dont_have_permission_to_perform_this_action": "Você não tem permissão para realizar esta ação. Verifique com um administrador do espaço de trabalho.", "Jump_to_message": "Ir para mensagem", "Missed_call": "Chamada perdida", + "Microphone_access_needed_to_record_audio": "Acesso ao microfone necessário para gravar áudio", + "Go_to_your_device_settings_and_allow_microphone": "Vá para as configurações do seu dispositivo e permita o acesso ao microfone pelo aplicativo Rocket.Chat", "In_app_message_notifications": "Notificações de mensagens in-app", "Vibrate": "Vibrar" } \ No newline at end of file diff --git a/app/lib/methods/helpers/openAppSettings.ts b/app/lib/methods/helpers/openAppSettings.ts new file mode 100644 index 000000000..5dea437dd --- /dev/null +++ b/app/lib/methods/helpers/openAppSettings.ts @@ -0,0 +1,11 @@ +import { Linking } from 'react-native'; + +import { isIOS } from './deviceInfo'; + +export const openAppSettings = (): void => { + if (isIOS) { + Linking.openURL('app-settings:'); + } else { + Linking.openSettings(); + } +};