diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index c60b0b132..e8412f926 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -1,11 +1,9 @@ -import { InfiniteData } from '@tanstack/react-query'; - import { getSettings } from 'soapbox/actions/settings'; import messages from 'soapbox/locales/messages'; -import { normalizeChatMessage } from 'soapbox/normalizers'; import { ChatKeys, IChat, isLastMessage } from 'soapbox/queries/chats'; import { queryClient } from 'soapbox/queries/client'; -import { updatePageItem, appendPageItem, removePageItem, flattenPages, PaginatedResult, sortQueryData } from 'soapbox/utils/queries'; +import { updateChatListItem } from 'soapbox/utils/chats'; +import { removePageItem } from 'soapbox/utils/queries'; // import { play, soundCache } from 'soapbox/utils/sounds'; import { connectStream } from '../stream'; @@ -30,7 +28,7 @@ import { } from './timelines'; import type { AppDispatch, RootState } from 'soapbox/store'; -import type { APIEntity, Chat, ChatMessage } from 'soapbox/types/entities'; +import type { APIEntity, Chat } from 'soapbox/types/entities'; const STREAMING_CHAT_UPDATE = 'STREAMING_CHAT_UPDATE'; const STREAMING_FOLLOW_RELATIONSHIPS_UPDATE = 'STREAMING_FOLLOW_RELATIONSHIPS_UPDATE'; @@ -52,43 +50,6 @@ const updateFollowRelationships = (relationships: APIEntity) => }); }; -interface ChatPayload extends Omit { - last_message: ChatMessage | null, -} - -const dateComparator = (chatA: IChat, chatB: IChat): number => { - const chatADate = new Date(chatA.last_message?.created_at as string); - const chatBDate = new Date(chatB.last_message?.created_at as string); - - if (chatBDate < chatADate) return -1; - if (chatBDate > chatADate) return 1; - return 0; -}; - -const updateChat = (payload: ChatPayload) => { - const { id: chatId, last_message: lastMessage } = payload; - - const currentChats = flattenPages( - queryClient.getQueryData>>(ChatKeys.chatSearch()), - ); - - if (currentChats?.find((chat: any) => chat.id === chatId)) { - // If the chat exists in the client, let's update it. - updatePageItem(ChatKeys.chatSearch(), payload as any, (o, n) => o.id === n.id); - // Now that we have the new chat loaded, let's re-sort to put - // the most recent on top. - sortQueryData(ChatKeys.chatSearch(), dateComparator); - } else { - // If this is a brand-new chat, let's invalid the queries. - queryClient.invalidateQueries(ChatKeys.chatSearch()); - } - - if (lastMessage) { - // Update the Chat Messages query data. - appendPageItem(ChatKeys.chatMessages(payload.id), normalizeChatMessage(lastMessage)); - } -}; - const removeChatMessage = (payload: string) => { const data = JSON.parse(payload); const chatId = data.chat_id; @@ -178,7 +139,7 @@ const connectTimelineStream = ( // Don't update own messages from streaming if (!messageOwned) { - updateChat(chat); + updateChatListItem(chat); // Temp disable until we support disabling/enabling. // play(soundCache.chat); } diff --git a/app/soapbox/features/chats/components/chat-message-list.tsx b/app/soapbox/features/chats/components/chat-message-list.tsx index 57ef60b6b..049fbb322 100644 --- a/app/soapbox/features/chats/components/chat-message-list.tsx +++ b/app/soapbox/features/chats/components/chat-message-list.tsx @@ -434,7 +434,7 @@ const ChatMessageList: React.FC = ({ chat }) => { if (isLoading) { return ( -
+
diff --git a/app/soapbox/features/ui/index.tsx b/app/soapbox/features/ui/index.tsx index f4e863d72..103b7698a 100644 --- a/app/soapbox/features/ui/index.tsx +++ b/app/soapbox/features/ui/index.tsx @@ -446,10 +446,6 @@ const UI: React.FC = ({ children }) => { dispatch(fetchAnnouncements()); - if (features.chats) { - // dispatch(fetchChats()); - } - if (account.staff) { dispatch(fetchReports({ resolved: false })); dispatch(fetchUsers(['local', 'need_approval'])); diff --git a/app/soapbox/utils/chats.ts b/app/soapbox/utils/chats.ts new file mode 100644 index 000000000..a19bf789a --- /dev/null +++ b/app/soapbox/utils/chats.ts @@ -0,0 +1,74 @@ +import { InfiniteData } from '@tanstack/react-query'; + +import { normalizeChatMessage } from 'soapbox/normalizers'; +import { ChatKeys } from 'soapbox/queries/chats'; +import { queryClient } from 'soapbox/queries/client'; +import { Chat, ChatMessage } from 'soapbox/types/entities'; + +import { compareDate } from './comparators'; +import { appendPageItem, flattenPages, PaginatedResult, sortQueryData, updatePageItem } from './queries'; + +interface ChatPayload extends Omit { + last_message: ChatMessage | null, +} + +/** + * Update the Chat entity inside the ChatSearch query. + * @param newChat - Chat entity. + */ +const updateChatInChatSearchQuery = (newChat: ChatPayload) => { + updatePageItem(ChatKeys.chatSearch(), newChat as any, (o, n) => o.id === n.id); +}; + +/** + * Re-order the ChatSearch query by the last message timestamp. + */ +const reOrderChatListItems = () => { + sortQueryData(ChatKeys.chatSearch(), (chatA, chatB) => { + return compareDate(chatA.last_message?.created_at as string, chatB.last_message?.created_at as string); + }); +}; + +/** + * Check if a Chat entity exists within the cached ChatSearch query. + * @param chatId - String + * @returns Boolean + */ +const checkIfChatExists = (chatId: string) => { + const currentChats = flattenPages( + queryClient.getQueryData>>(ChatKeys.chatSearch()), + ); + + return currentChats?.find((chat: Chat) => chat.id === chatId); +}; + +/** + * Force a re-fetch of ChatSearch. + */ +const invalidateChatSearchQuery = () => { + queryClient.invalidateQueries(ChatKeys.chatSearch()); +}; + +const updateChatListItem = (newChat: ChatPayload) => { + const { id: chatId, last_message: lastMessage } = newChat; + + const isChatAlreadyLoaded = checkIfChatExists(chatId); + + if (isChatAlreadyLoaded) { + // If the chat exists in the client, let's update it. + updateChatInChatSearchQuery(newChat); + // Now that we have the new chat loaded, let's re-sort to put + // the most recent on top. + reOrderChatListItems(); + } else { + // If this is a brand-new chat, let's invalid the queries. + invalidateChatSearchQuery(); + } + + if (lastMessage) { + // Update the Chat Messages query data. + appendPageItem(ChatKeys.chatMessages(newChat.id), normalizeChatMessage(lastMessage)); + } +}; + +export { updateChatListItem }; \ No newline at end of file diff --git a/app/soapbox/utils/comparators.ts b/app/soapbox/utils/comparators.ts index 14c3ba4df..ed91510e8 100644 --- a/app/soapbox/utils/comparators.ts +++ b/app/soapbox/utils/comparators.ts @@ -20,4 +20,20 @@ function compareId(id1: string, id2: string) { } } -export { compareId }; \ No newline at end of file +/** + * Compare by dates, where most recent date is returned first. + * + * @param dateString1 - string that is parsable by Date + * @param dateString2 - string that is parsable by Date + * @returns 1 | -1 | 0 + */ +function compareDate(dateString1: string, dateString2: string) { + const date1 = new Date(dateString1); + const date2 = new Date(dateString2); + + if (date2 < date1) return -1; + if (date2 > date1) return 1; + return 0; +} + +export { compareId, compareDate }; \ No newline at end of file