Account for API validation errors

This commit is contained in:
Chewbacca 2022-11-14 11:22:45 -05:00
parent d5fc6fe252
commit caa3873821
2 changed files with 39 additions and 14 deletions

View File

@ -10,7 +10,6 @@ import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({ const messages = defineMessages({
placeholder: { id: 'chat.input.placeholder', defaultMessage: 'Type a message' }, placeholder: { id: 'chat.input.placeholder', defaultMessage: 'Type a message' },
send: { id: 'chat.actions.send', defaultMessage: 'Send' }, send: { id: 'chat.actions.send', defaultMessage: 'Send' },
failedToSend: { id: 'chat.failed_to_send', defaultMessage: 'Message failed to send.' },
retry: { id: 'chat.retry', defaultMessage: 'Retry?' }, retry: { id: 'chat.retry', defaultMessage: 'Retry?' },
blocked: { id: 'chat_message_list.blocked', defaultMessage: 'You blocked this user' }, blocked: { id: 'chat_message_list.blocked', defaultMessage: 'You blocked this user' },
unblock: { id: 'chat_composer.unblock', defaultMessage: 'Unblock' }, unblock: { id: 'chat_composer.unblock', defaultMessage: 'Unblock' },
@ -20,9 +19,9 @@ const messages = defineMessages({
}); });
interface IChatComposer extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onKeyDown' | 'onChange' | 'disabled'> { interface IChatComposer extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onKeyDown' | 'onChange' | 'disabled'> {
value: string, value: string
onSubmit: () => void, onSubmit: () => void
hasErrorSubmittingMessage?: boolean, errorMessage: string | undefined
} }
/** Textarea input for chats. */ /** Textarea input for chats. */
@ -31,7 +30,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
onChange, onChange,
value, value,
onSubmit, onSubmit,
hasErrorSubmittingMessage = false, errorMessage = false,
disabled = false, disabled = false,
}, ref) => { }, ref) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -102,10 +101,10 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
</HStack> </HStack>
<HStack alignItems='center' className='h-5' space={1}> <HStack alignItems='center' className='h-5' space={1}>
{hasErrorSubmittingMessage && ( {errorMessage && (
<> <>
<Text theme='danger' size='xs'> <Text theme='danger' size='xs'>
{intl.formatMessage(messages.failedToSend)} {errorMessage}
</Text> </Text>
<button onClick={onSubmit} className='flex hover:underline'> <button onClick={onSubmit} className='flex hover:underline'>

View File

@ -1,5 +1,7 @@
import { AxiosError } from 'axios';
import classNames from 'clsx'; import classNames from 'clsx';
import React, { MutableRefObject, useEffect, useState } from 'react'; import React, { MutableRefObject, useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Stack } from 'soapbox/components/ui'; import { Stack } from 'soapbox/components/ui';
// import UploadProgress from 'soapbox/components/upload-progress'; // import UploadProgress from 'soapbox/components/upload-progress';
@ -12,17 +14,39 @@ import ChatMessageList from './chat-message-list';
// const fileKeyGen = (): number => Math.floor((Math.random() * 0x10000)); // const fileKeyGen = (): number => Math.floor((Math.random() * 0x10000));
const messages = defineMessages({
failedToSend: { id: 'chat.failed_to_send', defaultMessage: 'Message failed to send.' },
});
interface ChatInterface { interface ChatInterface {
chat: IChat, chat: IChat,
inputRef?: MutableRefObject<HTMLTextAreaElement | null>, inputRef?: MutableRefObject<HTMLTextAreaElement | null>,
className?: string, className?: string,
} }
/**
* Clears the value of the input while dispatching the `onChange` function
* which allows the <Textarea> to resize itself (this is important)
* because we autoGrow the element as the user inputs text that spans
* beyond one line
*/
const clearNativeInputValue = (element: HTMLTextAreaElement) => {
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set;
if (nativeInputValueSetter) {
nativeInputValueSetter.call(element, '');
const ev2 = new Event('input', { bubbles: true });
element.dispatchEvent(ev2);
}
};
/** /**
* Chat UI with just the messages and textarea. * Chat UI with just the messages and textarea.
* Reused between floating desktop chats and fullscreen/mobile chats. * Reused between floating desktop chats and fullscreen/mobile chats.
*/ */
const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => { const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
const intl = useIntl();
const { createChatMessage, acceptChat } = useChatActions(chat.id); const { createChatMessage, acceptChat } = useChatActions(chat.id);
const [content, setContent] = useState<string>(''); const [content, setContent] = useState<string>('');
@ -30,20 +54,19 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
// const [isUploading, setIsUploading] = useState(false); // const [isUploading, setIsUploading] = useState(false);
// const [uploadProgress, setUploadProgress] = useState(0); // const [uploadProgress, setUploadProgress] = useState(0);
// const [resetFileKey, setResetFileKey] = useState<number>(fileKeyGen()); // const [resetFileKey, setResetFileKey] = useState<number>(fileKeyGen());
const [hasErrorSubmittingMessage, setErrorSubmittingMessage] = useState<boolean>(false); const [errorMessage, setErrorMessage] = useState<string>();
const isSubmitDisabled = content.length === 0 && !attachment; const isSubmitDisabled = content.length === 0 && !attachment;
const submitMessage = () => { const submitMessage = () => {
createChatMessage.mutate({ chatId: chat.id, content }, { createChatMessage.mutate({ chatId: chat.id, content }, {
onSuccess: () => { onSuccess: () => {
setErrorSubmittingMessage(false); setErrorMessage(undefined);
}, },
onError: (error, _variables, context: any) => { onError: (error: AxiosError<{ error: string }>, _variables, context) => {
console.log(context); const message = error.response?.data?.error;
setErrorMessage(message || intl.formatMessage(messages.failedToSend));
setContent(context.prevContent as string); setContent(context.prevContent as string);
setErrorSubmittingMessage(true);
}, },
}); });
@ -51,6 +74,9 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
}; };
const clearState = () => { const clearState = () => {
if (inputRef?.current) {
clearNativeInputValue(inputRef.current);
}
setContent(''); setContent('');
setAttachment(undefined); setAttachment(undefined);
// setIsUploading(false); // setIsUploading(false);
@ -172,7 +198,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
value={content} value={content}
onChange={handleContentChange} onChange={handleContentChange}
onSubmit={sendMessage} onSubmit={sendMessage}
hasErrorSubmittingMessage={hasErrorSubmittingMessage} errorMessage={errorMessage}
/> />
</Stack> </Stack>
// {renderAttachment()} // {renderAttachment()}