Account for API validation errors
This commit is contained in:
parent
d5fc6fe252
commit
caa3873821
|
@ -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'>
|
||||||
|
|
|
@ -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()}
|
||||||
|
|
Loading…
Reference in New Issue