From e5055f0b23edb7eadbe8bbfc0b639f8607b9adef Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Mon, 13 Feb 2023 21:22:28 -0300 Subject: [PATCH] Chore: Refactor `AudioMessageRecorder` (#28019) --- .../AudioMessageRecorder.tsx | 95 ++++++++----------- .../RoomComposer/hooks/useAutoGrow.ts | 4 + .../body/composer/messageBox/MessageBox.tsx | 12 ++- .../messageBox/actions/AudioMessageAction.tsx | 31 ++++++ 4 files changed, 80 insertions(+), 62 deletions(-) create mode 100644 apps/meteor/client/views/room/components/body/composer/messageBox/actions/AudioMessageAction.tsx diff --git a/apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx b/apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx index 488dc9d55e3d..f64482202967 100644 --- a/apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx +++ b/apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx @@ -15,14 +15,13 @@ const audioRecorder = new AudioRecorder(); type AudioMessageRecorderProps = { rid: IRoom['_id']; tmid?: IMessage['_id']; - isRecording?: boolean; chatContext?: ChatAPI; // TODO: remove this when the composer is migrated to React } & Omit, 'is'>; -const AudioMessageRecorder = ({ rid, chatContext, isRecording, ...props }: AudioMessageRecorderProps): ReactElement | null => { +const AudioMessageRecorder = ({ rid, chatContext }: AudioMessageRecorderProps): ReactElement | null => { const t = useTranslation(); - const [state, setState] = useState<'idle' | 'loading' | 'recording'>('idle'); + const [state, setState] = useState<'loading' | 'recording'>('recording'); const [time, setTime] = useState('00:00'); const [isMicrophoneDenied, setIsMicrophoneDenied] = useState(false); const [recordingInterval, setRecordingInterval] = useState | null>(null); @@ -43,8 +42,6 @@ const AudioMessageRecorder = ({ rid, chatContext, isRecording, ...props }: Audio chat?.composer?.setRecordingMode(false); - setState('idle'); - return blob; }); @@ -83,44 +80,10 @@ const AudioMessageRecorder = ({ rid, chatContext, isRecording, ...props }: Audio } }); - useEffect(() => { - handleMount(); - - return () => { - handleUnmount(); - }; - }, [handleMount, handleUnmount]); - - const isFileUploadEnabled = useSetting('FileUpload_Enabled') as boolean; - const isAudioRecorderEnabled = useSetting('Message_AudioRecorderEnabled') as boolean; - const fileUploadMediaTypeBlackList = useSetting('FileUpload_MediaTypeBlackList') as string; - const fileUploadMediaTypeWhiteList = useSetting('FileUpload_MediaTypeWhiteList') as string; - - const isAllowed = useMemo( - () => - audioRecorder.isSupported() && - !isMicrophoneDenied && - isFileUploadEnabled && - isAudioRecorderEnabled && - (!fileUploadMediaTypeBlackList || !fileUploadMediaTypeBlackList.match(/audio\/mp3|audio\/\*/i)) && - (!fileUploadMediaTypeWhiteList || fileUploadMediaTypeWhiteList.match(/audio\/mp3|audio\/\*/i)), - [fileUploadMediaTypeBlackList, fileUploadMediaTypeWhiteList, isAudioRecorderEnabled, isFileUploadEnabled, isMicrophoneDenied], - ); - - const stateClass = useMemo(() => { - if (recordingRoomId && recordingRoomId !== rid) { - return 'rc-message-box__audio-message--busy'; - } - - return state && `rc-message-box__audio-message--${state}`; - }, [recordingRoomId, rid, state]); - - const handleRecordButtonClick = useMutableCallback(async () => { + const handleRecord = useMutableCallback(async () => { if (recordingRoomId && recordingRoomId !== rid) { return; } - chat?.composer?.setRecordingMode(true); - setState('recording'); try { await audioRecorder.start(); @@ -140,7 +103,6 @@ const AudioMessageRecorder = ({ rid, chatContext, isRecording, ...props }: Audio console.log(error); setIsMicrophoneDenied(true); chat?.composer?.setRecordingMode(false); - setState('idle'); } }); @@ -161,26 +123,45 @@ const AudioMessageRecorder = ({ rid, chatContext, isRecording, ...props }: Audio await chat?.flows.uploadFiles([file]); }); + useEffect(() => { + handleMount(); + handleRecord(); + + return () => { + handleUnmount(); + }; + }, [handleMount, handleUnmount, handleRecord]); + + const isFileUploadEnabled = useSetting('FileUpload_Enabled') as boolean; + const isAudioRecorderEnabled = useSetting('Message_AudioRecorderEnabled') as boolean; + const fileUploadMediaTypeBlackList = useSetting('FileUpload_MediaTypeBlackList') as string; + const fileUploadMediaTypeWhiteList = useSetting('FileUpload_MediaTypeWhiteList') as string; + + const isAllowed = useMemo( + () => + audioRecorder.isSupported() && + !isMicrophoneDenied && + isFileUploadEnabled && + isAudioRecorderEnabled && + (!fileUploadMediaTypeBlackList || !fileUploadMediaTypeBlackList.match(/audio\/mp3|audio\/\*/i)) && + (!fileUploadMediaTypeWhiteList || fileUploadMediaTypeWhiteList.match(/audio\/mp3|audio\/\*/i)), + [fileUploadMediaTypeBlackList, fileUploadMediaTypeWhiteList, isAudioRecorderEnabled, isFileUploadEnabled, isMicrophoneDenied], + ); + + const stateClass = useMemo(() => { + if (recordingRoomId && recordingRoomId !== rid) { + return 'rc-message-box__audio-message--busy'; + } + + return state && `rc-message-box__audio-message--${state}`; + }, [recordingRoomId, rid, state]); + if (!isAllowed) { return null; } - if (state === 'idle') { - return ( - - ); - } - return ( -
+ {state === 'recording' && ( <>
)} - + ); }; diff --git a/apps/meteor/client/views/room/components/body/composer/RoomComposer/hooks/useAutoGrow.ts b/apps/meteor/client/views/room/components/body/composer/RoomComposer/hooks/useAutoGrow.ts index e9ac8ee33387..4ce30df1bb3f 100644 --- a/apps/meteor/client/views/room/components/body/composer/RoomComposer/hooks/useAutoGrow.ts +++ b/apps/meteor/client/views/room/components/body/composer/RoomComposer/hooks/useAutoGrow.ts @@ -15,6 +15,7 @@ const shadowStyleBase: CSSProperties = { export const useAutoGrow = ( ref: RefObject, shadowRef: RefObject, + hideTextArea?: boolean, ): { textAreaStyle: CSSProperties; shadowStyle: CSSProperties; @@ -63,6 +64,9 @@ export const useAutoGrow = ( return { textAreaStyle: { + ...(hideTextArea && { + visibility: 'hidden', + }), whiteSpace: 'pre-wrap', wordWrap: 'break-word', overflowWrap: 'break-word', diff --git a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx index 3609ce83dac7..b37c2e2bf5fb 100644 --- a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx +++ b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx @@ -44,6 +44,7 @@ import { useAutoGrow } from '../RoomComposer/hooks/useAutoGrow'; import MessageBoxDropdown from './MessageBoxDropdown'; import MessageBoxFormattingToolbar from './MessageBoxFormattingToolbar'; import MessageBoxReplies from './MessageBoxReplies'; +import AudioMessageAction from './actions/AudioMessageAction'; import FileUploadAction from './actions/FileUploadAction'; import VideoMessageAction from './actions/VideoMessageAction'; @@ -270,7 +271,9 @@ const MessageBox = ({ subscribe: chat.composer?.formatters.subscribe ?? emptySubscribe, }); - const { textAreaStyle, shadowStyle } = useAutoGrow(textareaRef, shadowRef); + const isRecording = isRecordingAudio || isRecordingVideo; + + const { textAreaStyle, shadowStyle } = useAutoGrow(textareaRef, shadowRef, isRecordingAudio); const canSend = useReactiveValue(useCallback(() => roomCoordinator.verifyCanSendMessage(rid), [rid])); @@ -320,8 +323,6 @@ const MessageBox = ({ } }); - const isRecording = isRecordingAudio || isRecordingVideo; - return ( <> {chat?.composer?.quotedMessages && } @@ -334,6 +335,7 @@ const MessageBox = ({ )} {isRecordingVideo && } + {isRecordingAudio && } } aria-label={t('Message')} @@ -360,8 +362,8 @@ const MessageBox = ({ /> )} - - + + diff --git a/apps/meteor/client/views/room/components/body/composer/messageBox/actions/AudioMessageAction.tsx b/apps/meteor/client/views/room/components/body/composer/messageBox/actions/AudioMessageAction.tsx new file mode 100644 index 000000000000..ed6583c21b91 --- /dev/null +++ b/apps/meteor/client/views/room/components/body/composer/messageBox/actions/AudioMessageAction.tsx @@ -0,0 +1,31 @@ +import { MessageComposerAction } from '@rocket.chat/ui-composer'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +import type { ChatAPI } from '../../../../../../../lib/chats/ChatAPI'; +import { useChat } from '../../../../../contexts/ChatContext'; + +type AudioMessageActionProps = { + chatContext?: ChatAPI; +} & Omit, 'is'>; + +const AudioMessageAction = ({ chatContext, ...props }: AudioMessageActionProps) => { + const t = useTranslation(); + const chat = useChat() ?? chatContext; + + const handleRecordButtonClick = () => chat?.composer?.setRecordingMode(true); + + return ( + + ); +}; + +export default AudioMessageAction;