Refactor 'createChatMessage' mutation

revert-fa4bd20d
Chewbacca 2022-11-09 12:24:44 -05:00
rodzic 2460d85460
commit e7106a35b3
3 zmienionych plików z 104 dodań i 62 usunięć

Wyświetl plik

@ -1,13 +1,10 @@
import { useMutation } from '@tanstack/react-query';
import classNames from 'clsx'; import classNames from 'clsx';
import React, { MutableRefObject, useEffect, useState } from 'react'; import React, { MutableRefObject, useEffect, useState } from 'react';
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';
// import UploadButton from 'soapbox/features/compose/components/upload_button'; // import UploadButton from 'soapbox/features/compose/components/upload_button';
import { useOwnAccount } from 'soapbox/hooks'; import { IChat, useChatActions } from 'soapbox/queries/chats';
import { ChatKeys, IChat, useChatActions } from 'soapbox/queries/chats';
import { queryClient } from 'soapbox/queries/client';
// import { truncateFilename } from 'soapbox/utils/media'; // import { truncateFilename } from 'soapbox/utils/media';
import ChatComposer from './chat-composer'; import ChatComposer from './chat-composer';
@ -26,8 +23,6 @@ interface ChatInterface {
* 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 account = useOwnAccount();
const { createChatMessage, acceptChat } = useChatActions(chat.id); const { createChatMessage, acceptChat } = useChatActions(chat.id);
const [content, setContent] = useState<string>(''); const [content, setContent] = useState<string>('');
@ -39,57 +34,19 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
const isSubmitDisabled = content.length === 0 && !attachment; const isSubmitDisabled = content.length === 0 && !attachment;
const submitMessage = useMutation(({ chatId, content }: any) => createChatMessage(chatId, content), { const submitMessage = () => {
retry: false, createChatMessage.mutate({ chatId: chat.id, content }, {
onMutate: async (newMessage: any) => { onError: (_error, _variables, context: any) => {
// Cancel any outgoing refetches (so they don't overwrite our optimistic update) setContent(context.prevContent as string);
await queryClient.cancelQueries(['chats', 'messages', chat.id]); setErrorSubmittingMessage(true);
},
onSuccess: () => {
setErrorSubmittingMessage(false);
},
});
// Snapshot the previous value clearState();
const prevChatMessages = queryClient.getQueryData(['chats', 'messages', chat.id]); };
const prevContent = content;
// Clear state (content, attachment, etc)
clearState();
// Optimistically update to the new value
queryClient.setQueryData(['chats', 'messages', chat.id], (prevResult: any) => {
const newResult = { ...prevResult };
newResult.pages = newResult.pages.map((page: any, idx: number) => {
if (idx === 0) {
return {
...page,
result: [...page.result, {
...newMessage,
id: String(Number(new Date())),
created_at: new Date(),
account_id: account?.id,
pending: true,
unread: true,
}],
};
}
return page;
});
return newResult;
});
// Return a context object with the snapshotted value
return { prevChatMessages, prevContent };
},
// If the mutation fails, use the context returned from onMutate to roll back
onError: (_error: any, _newData: any, context: any) => {
setContent(context.prevContent);
queryClient.setQueryData(['chats', 'messages', chat.id], context.prevChatMessages);
setErrorSubmittingMessage(true);
},
onSuccess: () => {
setErrorSubmittingMessage(false);
queryClient.invalidateQueries(ChatKeys.chatMessages(chat.id));
},
});
const clearState = () => { const clearState = () => {
setContent(''); setContent('');
@ -101,8 +58,8 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
}; };
const sendMessage = () => { const sendMessage = () => {
if (!isSubmitDisabled && !submitMessage.isLoading) { if (!isSubmitDisabled && !createChatMessage.isLoading) {
submitMessage.mutate({ chatId: chat.id, content }); submitMessage();
if (!chat.accepted) { if (!chat.accepted) {
acceptChat.mutate(); acceptChat.mutate();

Wyświetl plik

@ -295,6 +295,43 @@ describe('useChatActions', () => {
}); });
}); });
describe('createChatMessage()', () => {
beforeEach(() => {
const initialQueryData = {
pages: [
{ result: [buildChatMessage('1')], hasMore: false, link: undefined },
],
pageParams: [undefined],
};
queryClient.setQueryData(ChatKeys.chatMessages(chat.id), initialQueryData);
__stub((mock) => {
mock
.onPost(`/api/v1/pleroma/chats/${chat.id}/messages`)
.reply(200, { hello: 'world' });
});
});
it('creates a chat message', async() => {
const { result } = renderHook(() => {
const { createChatMessage } = useChatActions(chat.id);
useEffect(() => {
createChatMessage.mutate({ chatId: chat.id, content: 'hello' });
}, []);
return createChatMessage;
});
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});
expect(result.current.data.data).toEqual({ hello: 'world' });
});
});
describe('updateChat()', () => { describe('updateChat()', () => {
const nextUnreadCount = 5; const nextUnreadCount = 5;

Wyświetl plik

@ -8,7 +8,7 @@ import { getNextLink } from 'soapbox/api';
import compareId from 'soapbox/compare_id'; import compareId from 'soapbox/compare_id';
import { ChatWidgetScreens, useChatContext } from 'soapbox/contexts/chat-context'; import { ChatWidgetScreens, useChatContext } from 'soapbox/contexts/chat-context';
import { useStatContext } from 'soapbox/contexts/stat-context'; import { useStatContext } from 'soapbox/contexts/stat-context';
import { useApi, useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { useApi, useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks';
import { normalizeChatMessage } from 'soapbox/normalizers'; import { normalizeChatMessage } from 'soapbox/normalizers';
import { flattenPages, PaginatedResult, updatePageItem } from 'soapbox/utils/queries'; import { flattenPages, PaginatedResult, updatePageItem } from 'soapbox/utils/queries';
@ -202,8 +202,10 @@ const useChat = (chatId?: string) => {
}; };
const useChatActions = (chatId: string) => { const useChatActions = (chatId: string) => {
const account = useOwnAccount();
const api = useApi(); const api = useApi();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { setUnreadChatsCount } = useStatContext(); const { setUnreadChatsCount } = useStatContext();
const { chat, changeScreen } = useChatContext(); const { chat, changeScreen } = useChatContext();
@ -230,9 +232,55 @@ const useChatActions = (chatId: string) => {
.catch(() => null); .catch(() => null);
}; };
const createChatMessage = (chatId: string, content: string) => { const createChatMessage = useMutation(
return api.post<IChat>(`/api/v1/pleroma/chats/${chatId}/messages`, { content }); (
}; { chatId, content }: { chatId: string, content: string },
) => api.post<IChat>(`/api/v1/pleroma/chats/${chatId}/messages`, { content }),
{
retry: false,
onMutate: async (variables) => {
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
await queryClient.cancelQueries(['chats', 'messages', variables.chatId]);
// Snapshot the previous value
const prevContent = variables.content;
const prevChatMessages = queryClient.getQueryData(['chats', 'messages', variables.chatId]);
// Optimistically update to the new value
queryClient.setQueryData(ChatKeys.chatMessages(variables.chatId), (prevResult: any) => {
const newResult = { ...prevResult };
newResult.pages = newResult.pages.map((page: any, idx: number) => {
if (idx === 0) {
return {
...page,
result: [...page.result, {
content: variables.content,
id: String(Number(new Date())),
created_at: new Date(),
account_id: account?.id,
pending: true,
unread: true,
}],
};
}
return page;
});
return newResult;
});
return { prevChatMessages, prevContent };
},
// If the mutation fails, use the context returned from onMutate to roll back
onError: (_error: any, variables, context: any) => {
queryClient.setQueryData(['chats', 'messages', variables.chatId], context.prevChatMessages);
},
onSuccess: (_data: any, variables) => {
queryClient.invalidateQueries(ChatKeys.chatMessages(variables.chatId));
},
},
);
const updateChat = useMutation((data: UpdateChatVariables) => api.patch<IChat>(`/api/v1/pleroma/chats/${chatId}`, data), { const updateChat = useMutation((data: UpdateChatVariables) => api.patch<IChat>(`/api/v1/pleroma/chats/${chatId}`, data), {
onMutate: async (data) => { onMutate: async (data) => {