kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Error handling for bad sends
rodzic
6b38e37019
commit
c48b4adc81
|
@ -1,5 +1,5 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
import { fetchChat, markChatRead } from 'soapbox/actions/chats';
|
import { fetchChat, markChatRead } from 'soapbox/actions/chats';
|
||||||
import { Column } from 'soapbox/components/ui';
|
import { Column } from 'soapbox/components/ui';
|
||||||
|
@ -22,22 +22,12 @@ interface IChatRoom {
|
||||||
const ChatRoom: React.FC<IChatRoom> = ({ params }) => {
|
const ChatRoom: React.FC<IChatRoom> = ({ params }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const displayFqn = useAppSelector(getDisplayFqn);
|
const displayFqn = useAppSelector(getDisplayFqn);
|
||||||
const inputElem = useRef<HTMLTextAreaElement | null>(null);
|
|
||||||
|
|
||||||
const chat = useAppSelector(state => {
|
const chat = useAppSelector(state => {
|
||||||
const chat = state.chats.items.get(params.chatId, ImmutableMap()).toJS() as any;
|
const chat = state.chats.items.get(params.chatId, ImmutableMap()).toJS() as any;
|
||||||
return getChat(state, chat);
|
return getChat(state, chat);
|
||||||
});
|
});
|
||||||
|
|
||||||
const focusInput = () => {
|
|
||||||
inputElem.current?.focus();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputRef = (el: HTMLTextAreaElement) => {
|
|
||||||
inputElem.current = el;
|
|
||||||
focusInput();
|
|
||||||
};
|
|
||||||
|
|
||||||
const markRead = () => {
|
const markRead = () => {
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
dispatch(markChatRead(chat.id));
|
dispatch(markChatRead(chat.id));
|
||||||
|
@ -57,11 +47,7 @@ const ChatRoom: React.FC<IChatRoom> = ({ params }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column label={`@${getAcct(chat.account as any, displayFqn)}`}>
|
<Column label={`@${getAcct(chat.account as any, displayFqn)}`}>
|
||||||
<ChatBox
|
<ChatBox chat={chat as any} autosize />
|
||||||
chat={chat as any}
|
|
||||||
onSetInputRef={handleInputRef}
|
|
||||||
autosize
|
|
||||||
/>
|
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +1,13 @@
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
import React, { MutableRefObject, useState } from 'react';
|
||||||
import React, { MutableRefObject, useEffect, useRef, useState } from 'react';
|
|
||||||
import { useIntl, defineMessages } from 'react-intl';
|
import { useIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import {
|
|
||||||
sendChatMessage,
|
|
||||||
markChatRead,
|
|
||||||
} from 'soapbox/actions/chats';
|
|
||||||
import { uploadMedia } from 'soapbox/actions/media';
|
import { uploadMedia } from 'soapbox/actions/media';
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { HStack, IconButton, Stack, Text, Textarea } from 'soapbox/components/ui';
|
||||||
import { initReport } from 'soapbox/actions/reports';
|
|
||||||
import { Avatar, Button, HStack, Icon, IconButton, Input, Stack, Text, Textarea } from 'soapbox/components/ui';
|
|
||||||
import UploadProgress from 'soapbox/components/upload-progress';
|
import UploadProgress from 'soapbox/components/upload-progress';
|
||||||
import UploadButton from 'soapbox/features/compose/components/upload_button';
|
import UploadButton from 'soapbox/features/compose/components/upload_button';
|
||||||
import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
||||||
import { IChat, IChatMessage, useChat } from 'soapbox/queries/chats';
|
import { IChat, useChat } from 'soapbox/queries/chats';
|
||||||
import { queryClient } from 'soapbox/queries/client';
|
import { queryClient } from 'soapbox/queries/client';
|
||||||
import { truncateFilename } from 'soapbox/utils/media';
|
import { truncateFilename } from 'soapbox/utils/media';
|
||||||
|
|
||||||
|
@ -29,7 +22,6 @@ const fileKeyGen = (): number => Math.floor((Math.random() * 0x10000));
|
||||||
|
|
||||||
interface IChatBox {
|
interface IChatBox {
|
||||||
chat: IChat,
|
chat: IChat,
|
||||||
onSetInputRef: (el: HTMLTextAreaElement) => void,
|
|
||||||
autosize?: boolean,
|
autosize?: boolean,
|
||||||
inputRef?: MutableRefObject<HTMLTextAreaElement>
|
inputRef?: MutableRefObject<HTMLTextAreaElement>
|
||||||
}
|
}
|
||||||
|
@ -38,21 +30,19 @@ interface IChatBox {
|
||||||
* 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 ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }) => {
|
const ChatBox: React.FC<IChatBox> = ({ chat, autosize, inputRef }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const chatMessageIds = useAppSelector(state => state.chat_message_lists.get(chat.id, ImmutableOrderedSet<string>()));
|
|
||||||
const account = useOwnAccount();
|
const account = useOwnAccount();
|
||||||
|
|
||||||
const { createChatMessage, markChatAsRead, acceptChat } = useChat(chat.id);
|
const { createChatMessage, acceptChat } = useChat(chat.id);
|
||||||
|
|
||||||
const [content, setContent] = useState<string>('');
|
const [content, setContent] = useState<string>('');
|
||||||
const [attachment, setAttachment] = useState<any>(undefined);
|
const [attachment, setAttachment] = useState<any>(undefined);
|
||||||
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 inputElem = useRef<HTMLTextAreaElement | null>(null);
|
|
||||||
|
|
||||||
const isSubmitDisabled = content.length === 0 && !attachment;
|
const isSubmitDisabled = content.length === 0 && !attachment;
|
||||||
|
|
||||||
|
@ -99,6 +89,7 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }
|
||||||
onError: (_error: any, _newData: any, context: any) => {
|
onError: (_error: any, _newData: any, context: any) => {
|
||||||
setContent(context.prevContent);
|
setContent(context.prevContent);
|
||||||
queryClient.setQueryData(['chats', 'messages', chat.id], context.prevChatMessages);
|
queryClient.setQueryData(['chats', 'messages', chat.id], context.prevChatMessages);
|
||||||
|
setErrorSubmittingMessage(true);
|
||||||
},
|
},
|
||||||
// Always refetch after error or success:
|
// Always refetch after error or success:
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
@ -112,6 +103,7 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
setUploadProgress(0);
|
setUploadProgress(0);
|
||||||
setResetFileKey(fileKeyGen());
|
setResetFileKey(fileKeyGen());
|
||||||
|
setErrorSubmittingMessage(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendMessage = () => {
|
const sendMessage = () => {
|
||||||
|
@ -217,11 +209,11 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }
|
||||||
<Stack className='overflow-hidden flex flex-grow' onMouseOver={handleMouseOver}>
|
<Stack className='overflow-hidden flex flex-grow' onMouseOver={handleMouseOver}>
|
||||||
<div className='flex-grow h-full overflow-hidden flex justify-center'>
|
<div className='flex-grow h-full overflow-hidden flex justify-center'>
|
||||||
<ChatMessageList chat={chat} autosize />
|
<ChatMessageList chat={chat} autosize />
|
||||||
</div >
|
</div>
|
||||||
|
|
||||||
<div className='mt-auto p-4 shadow-3xl'>
|
<div className='mt-auto pt-4 px-4 shadow-3xl'>
|
||||||
<HStack alignItems='center' justifyContent='between' space={4}>
|
<HStack alignItems='center' justifyContent='between' space={4}>
|
||||||
<div className='flex-grow'>
|
<Stack grow>
|
||||||
<Textarea
|
<Textarea
|
||||||
autoFocus
|
autoFocus
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
@ -233,7 +225,7 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }
|
||||||
autoGrow
|
autoGrow
|
||||||
maxRows={5}
|
maxRows={5}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Stack>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
src={require('@tabler/icons/send.svg')}
|
src={require('@tabler/icons/send.svg')}
|
||||||
|
@ -243,8 +235,24 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }
|
||||||
onClick={sendMessage}
|
onClick={sendMessage}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
|
<HStack alignItems='center' className='h-5' space={1}>
|
||||||
|
{hasErrorSubmittingMessage && (
|
||||||
|
<>
|
||||||
|
<Text theme='danger' size='xs'>
|
||||||
|
Message failed to send.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<button onClick={sendMessage} className='flex hover:underline'>
|
||||||
|
<Text theme='primary' size='xs' tag='span'>
|
||||||
|
Retry?
|
||||||
|
</Text>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
</div>
|
</div>
|
||||||
</Stack >
|
</Stack>
|
||||||
// {renderAttachment()}
|
// {renderAttachment()}
|
||||||
// {isUploading && (
|
// {isUploading && (
|
||||||
// <UploadProgress progress={uploadProgress * 100} />
|
// <UploadProgress progress={uploadProgress * 100} />
|
||||||
|
|
|
@ -74,7 +74,7 @@ const ChatWindow = () => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack className='overflow-hidden flex-grow h-full' space={2}>
|
<Stack className='overflow-hidden flex-grow h-full' space={2}>
|
||||||
<ChatBox chat={chat} inputRef={inputRef as any} onSetInputRef={() => null} />
|
<ChatBox chat={chat} inputRef={inputRef as any} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import RelativeTimestamp from 'soapbox/components/relative_timestamp';
|
import RelativeTimestamp from 'soapbox/components/relative-timestamp';
|
||||||
import { Avatar, HStack, Stack, Text } from 'soapbox/components/ui';
|
import { Avatar, HStack, Stack, Text } from 'soapbox/components/ui';
|
||||||
import VerificationBadge from 'soapbox/components/verification_badge';
|
import VerificationBadge from 'soapbox/components/verification_badge';
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue