Skip to content

Commit

Permalink
Chore: Refactor AudioMessageRecorder (#28019)
Browse files Browse the repository at this point in the history
  • Loading branch information
dougfabris committed Feb 14, 2023
1 parent d989e3a commit e5055f0
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 62 deletions.
Expand Up @@ -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<AllHTMLAttributes<HTMLDivElement>, '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<ReturnType<typeof setInterval> | null>(null);
Expand All @@ -43,8 +42,6 @@ const AudioMessageRecorder = ({ rid, chatContext, isRecording, ...props }: Audio

chat?.composer?.setRecordingMode(false);

setState('idle');

return blob;
});

Expand Down Expand Up @@ -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();
Expand All @@ -140,7 +103,6 @@ const AudioMessageRecorder = ({ rid, chatContext, isRecording, ...props }: Audio
console.log(error);
setIsMicrophoneDenied(true);
chat?.composer?.setRecordingMode(false);
setState('idle');
}
});

Expand All @@ -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 (
<MessageComposerAction
disabled={isRecording}
title={t('Audio_message')}
icon='mic'
className='rc-message-box__icon rc-message-box__audio-message-mic'
data-qa-id='audio-record'
onClick={handleRecordButtonClick}
{...props}
/>
);
}

return (
<div className={`rc-message-box__audio-message ${stateClass}`}>
<Box position='absolute' pi='x4' pb='x12' className={`rc-message-box__audio-message ${stateClass}`}>
{state === 'recording' && (
<>
<MessageComposerAction
Expand All @@ -204,7 +185,7 @@ const AudioMessageRecorder = ({ rid, chatContext, isRecording, ...props }: Audio
<Throbber inheritColor size='x12' />
</div>
)}
</div>
</Box>
);
};

Expand Down
Expand Up @@ -15,6 +15,7 @@ const shadowStyleBase: CSSProperties = {
export const useAutoGrow = (
ref: RefObject<HTMLTextAreaElement>,
shadowRef: RefObject<HTMLTextAreaElement>,
hideTextArea?: boolean,
): {
textAreaStyle: CSSProperties;
shadowStyle: CSSProperties;
Expand Down Expand Up @@ -63,6 +64,9 @@ export const useAutoGrow = (

return {
textAreaStyle: {
...(hideTextArea && {
visibility: 'hidden',
}),
whiteSpace: 'pre-wrap',
wordWrap: 'break-word',
overflowWrap: 'break-word',
Expand Down
Expand Up @@ -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';

Expand Down Expand Up @@ -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]));

Expand Down Expand Up @@ -320,8 +323,6 @@ const MessageBox = ({
}
});

const isRecording = isRecordingAudio || isRecordingVideo;

return (
<>
{chat?.composer?.quotedMessages && <MessageBoxReplies />}
Expand All @@ -334,6 +335,7 @@ const MessageBox = ({
)}
{isRecordingVideo && <VideoMessageRecorder reference={messageComposerRef} rid={rid} tmid={tmid} />}
<MessageComposer ref={messageComposerRef} variant={isEditing ? 'editing' : undefined}>
{isRecordingAudio && <AudioMessageRecorder rid={rid} tmid={tmid} disabled={!canSend || typing} />}
<MessageComposerInput
ref={callbackRef as unknown as Ref<HTMLInputElement>}
aria-label={t('Message')}
Expand All @@ -360,8 +362,8 @@ const MessageBox = ({
/>
)}
<MessageComposerActionsDivider />
<VideoMessageAction isRecording={isRecordingAudio} />
<AudioMessageRecorder rid={rid} tmid={tmid} disabled={!canSend || typing || isRecordingVideo} />
<VideoMessageAction isRecording={isRecording} />
<AudioMessageAction disabled={!canSend || typing || isRecording} />
<FileUploadAction isRecording={isRecording} />
<MessageBoxDropdown isRecording={isRecording} rid={rid} tmid={tmid} />
</MessageComposerToolbarActions>
Expand Down
@@ -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<AllHTMLAttributes<HTMLButtonElement>, 'is'>;

const AudioMessageAction = ({ chatContext, ...props }: AudioMessageActionProps) => {
const t = useTranslation();
const chat = useChat() ?? chatContext;

const handleRecordButtonClick = () => chat?.composer?.setRecordingMode(true);

return (
<MessageComposerAction
title={t('Audio_message')}
icon='mic'
className='rc-message-box__icon rc-message-box__audio-message-mic'
data-qa-id='audio-record'
onClick={handleRecordButtonClick}
{...props}
/>
);
};

export default AudioMessageAction;

0 comments on commit e5055f0

Please sign in to comment.