Invalidate query cache when new chats come in
This commit is contained in:
parent
69d37a10fb
commit
c41b71c76c
9 changed files with 51 additions and 37 deletions
|
@ -1,7 +1,10 @@
|
||||||
|
import { InfiniteData } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
import { getSettings } from 'soapbox/actions/settings';
|
||||||
import messages from 'soapbox/locales/messages';
|
import messages from 'soapbox/locales/messages';
|
||||||
|
import { ChatKeys } from 'soapbox/queries/chats';
|
||||||
import { queryClient } from 'soapbox/queries/client';
|
import { queryClient } from 'soapbox/queries/client';
|
||||||
import { updatePageItem, appendPageItem } from 'soapbox/utils/queries';
|
import { updatePageItem, appendPageItem, flattenPages, PaginatedResult } from 'soapbox/utils/queries';
|
||||||
import { play, soundCache } from 'soapbox/utils/sounds';
|
import { play, soundCache } from 'soapbox/utils/sounds';
|
||||||
|
|
||||||
import { connectStream } from '../stream';
|
import { connectStream } from '../stream';
|
||||||
|
@ -55,11 +58,22 @@ interface ChatPayload extends Omit<Chat, 'last_message'> {
|
||||||
const updateChat = (payload: ChatPayload) => {
|
const updateChat = (payload: ChatPayload) => {
|
||||||
const { id: chatId, last_message: lastMessage } = payload;
|
const { id: chatId, last_message: lastMessage } = payload;
|
||||||
|
|
||||||
queryClient.setQueryData<Chat>(['chats', 'chat', chatId], payload as any);
|
const currentChats = flattenPages(queryClient.getQueryData<InfiniteData<PaginatedResult<unknown>>>(ChatKeys.chatSearch()));
|
||||||
updatePageItem<Chat>(['chats', 'search'], payload as any, (o, n) => o.id === n.id);
|
|
||||||
|
// Update the specific Chat query data.
|
||||||
|
queryClient.setQueryData<Chat>(ChatKeys.chat(chatId), payload as any);
|
||||||
|
|
||||||
|
if (currentChats?.find((chat: any) => chat.id === chatId)) {
|
||||||
|
// If the chat exists in the client, let's update it.
|
||||||
|
updatePageItem<Chat>(ChatKeys.chatSearch(), payload as any, (o, n) => o.id === n.id);
|
||||||
|
} else {
|
||||||
|
// If this is a brand-new chat, let's invalid the queries.
|
||||||
|
queryClient.invalidateQueries(ChatKeys.chatSearch());
|
||||||
|
}
|
||||||
|
|
||||||
if (lastMessage) {
|
if (lastMessage) {
|
||||||
appendPageItem(['chats', 'messages', payload.id], lastMessage);
|
// Update the Chat Messages query data.
|
||||||
|
appendPageItem(ChatKeys.chatMessages(payload.id), lastMessage);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Stack, HStack, Card, Avatar, Text, Icon } from 'soapbox/components/ui';
|
||||||
import IconButton from 'soapbox/components/ui/icon-button/icon-button';
|
import IconButton from 'soapbox/components/ui/icon-button/icon-button';
|
||||||
import StatusCard from 'soapbox/features/status/components/card';
|
import StatusCard from 'soapbox/features/status/components/card';
|
||||||
import { useAppSelector } from 'soapbox/hooks';
|
import { useAppSelector } from 'soapbox/hooks';
|
||||||
import { adKeys } from 'soapbox/queries/ads';
|
import { AdKeys } from 'soapbox/queries/ads';
|
||||||
|
|
||||||
import type { Card as CardEntity } from 'soapbox/types/entities';
|
import type { Card as CardEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ const Ad: React.FC<IAd> = ({ card, impression, expires }) => {
|
||||||
|
|
||||||
/** Invalidate query cache for ads. */
|
/** Invalidate query cache for ads. */
|
||||||
const bustCache = (): void => {
|
const bustCache = (): void => {
|
||||||
queryClient.invalidateQueries(adKeys.ads);
|
queryClient.invalidateQueries(AdKeys.ads);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Toggle the info box on click. */
|
/** Toggle the info box on click. */
|
||||||
|
@ -113,7 +113,7 @@ const Ad: React.FC<IAd> = ({ card, impression, expires }) => {
|
||||||
</Stack>
|
</Stack>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
<StatusCard card={card} onOpenMedia={() => {}} horizontal />
|
<StatusCard card={card} onOpenMedia={() => { }} horizontal />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import Bundle from 'soapbox/features/ui/components/bundle';
|
||||||
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
|
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
|
||||||
import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
||||||
import { normalizeAccount } from 'soapbox/normalizers';
|
import { normalizeAccount } from 'soapbox/normalizers';
|
||||||
import { chatKeys, IChat, IChatMessage, useChatActions, useChatMessages } from 'soapbox/queries/chats';
|
import { ChatKeys, IChat, IChatMessage, useChatActions, useChatMessages } from 'soapbox/queries/chats';
|
||||||
import { queryClient } from 'soapbox/queries/client';
|
import { queryClient } from 'soapbox/queries/client';
|
||||||
import { stripHTML } from 'soapbox/utils/html';
|
import { stripHTML } from 'soapbox/utils/html';
|
||||||
import { onlyEmoji } from 'soapbox/utils/rich_content';
|
import { onlyEmoji } from 'soapbox/utils/rich_content';
|
||||||
|
@ -96,7 +96,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
|
|
||||||
const handleDeleteMessage = useMutation((chatMessageId: string) => deleteChatMessage(chatMessageId), {
|
const handleDeleteMessage = useMutation((chatMessageId: string) => deleteChatMessage(chatMessageId), {
|
||||||
onSettled: () => {
|
onSettled: () => {
|
||||||
queryClient.invalidateQueries(chatKeys.chatMessages(chat.id));
|
queryClient.invalidateQueries(ChatKeys.chatMessages(chat.id));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { useChats } from 'soapbox/queries/chats';
|
||||||
import { queryClient } from 'soapbox/queries/client';
|
import { queryClient } from 'soapbox/queries/client';
|
||||||
import useAccountSearch from 'soapbox/queries/search';
|
import useAccountSearch from 'soapbox/queries/search';
|
||||||
|
|
||||||
import { chatKeys } from '../../../../queries/chats';
|
import { ChatKeys } from '../../../../queries/chats';
|
||||||
import ChatPaneHeader from '../chat-pane-header';
|
import ChatPaneHeader from '../chat-pane-header';
|
||||||
import { Pane } from '../ui';
|
import { Pane } from '../ui';
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ const ChatSearch = () => {
|
||||||
},
|
},
|
||||||
onSuccess: (response) => {
|
onSuccess: (response) => {
|
||||||
setChat(response.data);
|
setChat(response.data);
|
||||||
queryClient.invalidateQueries(chatKeys.chatSearch());
|
queryClient.invalidateQueries(ChatKeys.chatSearch());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { HStack, IconButton, 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 { useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
import { useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
||||||
import { chatKeys, IChat, useChatActions } from 'soapbox/queries/chats';
|
import { ChatKeys, IChat, useChatActions } 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';
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, autosize, inputRef, className })
|
||||||
},
|
},
|
||||||
// Always refetch after error or success:
|
// Always refetch after error or success:
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(chatKeys.chatMessages(chat.id));
|
queryClient.invalidateQueries(ChatKeys.chatMessages(chat.id));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,15 @@ import { Ad, getProvider } from 'soapbox/features/ads/providers';
|
||||||
import { useAppDispatch } from 'soapbox/hooks';
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
import { isExpired } from 'soapbox/utils/ads';
|
import { isExpired } from 'soapbox/utils/ads';
|
||||||
|
|
||||||
const adKeys = {
|
const AdKeys = {
|
||||||
ads: ['ads'] as const,
|
ads: ['ads'] as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
function useAds() {
|
function useAds() {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const getAds = async() => {
|
const getAds = async () => {
|
||||||
return dispatch(async(_, getState) => {
|
return dispatch(async (_, getState) => {
|
||||||
const provider = await getProvider(getState);
|
const provider = await getProvider(getState);
|
||||||
if (provider) {
|
if (provider) {
|
||||||
return provider.getAds(getState);
|
return provider.getAds(getState);
|
||||||
|
@ -22,7 +22,7 @@ function useAds() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = useQuery<Ad[]>(adKeys.ads, getAds, {
|
const result = useQuery<Ad[]>(AdKeys.ads, getAds, {
|
||||||
placeholderData: [],
|
placeholderData: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,4 +35,4 @@ function useAds() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useAds as default, adKeys };
|
export { useAds as default, AdKeys };
|
||||||
|
|
|
@ -52,7 +52,7 @@ export interface IChatSilence {
|
||||||
target_account_id: number
|
target_account_id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatKeys = {
|
const ChatKeys = {
|
||||||
chat: (chatId?: string) => ['chats', 'chat', chatId] as const,
|
chat: (chatId?: string) => ['chats', 'chat', chatId] as const,
|
||||||
chatMessages: (chatId: string) => ['chats', 'messages', chatId] as const,
|
chatMessages: (chatId: string) => ['chats', 'messages', chatId] as const,
|
||||||
chatSearch: (searchQuery?: string) => searchQuery ? ['chats', 'search', searchQuery] : ['chats', 'search'] as const,
|
chatSearch: (searchQuery?: string) => searchQuery ? ['chats', 'search', searchQuery] : ['chats', 'search'] as const,
|
||||||
|
@ -81,7 +81,7 @@ const useChatMessages = (chatId: string) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryInfo = useInfiniteQuery(chatKeys.chatMessages(chatId), ({ pageParam }) => getChatMessages(chatId, pageParam), {
|
const queryInfo = useInfiniteQuery(ChatKeys.chatMessages(chatId), ({ pageParam }) => getChatMessages(chatId, pageParam), {
|
||||||
getNextPageParam: (config) => {
|
getNextPageParam: (config) => {
|
||||||
if (config.hasMore) {
|
if (config.hasMore) {
|
||||||
return { link: config.link };
|
return { link: config.link };
|
||||||
|
@ -91,7 +91,7 @@ const useChatMessages = (chatId: string) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = flattenPages(queryInfo);
|
const data = flattenPages(queryInfo.data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...queryInfo,
|
...queryInfo,
|
||||||
|
@ -133,7 +133,7 @@ const useChats = (search?: string) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryInfo = useInfiniteQuery(chatKeys.chatSearch(search), ({ pageParam }) => getChats(pageParam), {
|
const queryInfo = useInfiniteQuery(ChatKeys.chatSearch(search), ({ pageParam }) => getChats(pageParam), {
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
getNextPageParam: (config) => {
|
getNextPageParam: (config) => {
|
||||||
if (config.hasMore) {
|
if (config.hasMore) {
|
||||||
|
@ -144,7 +144,7 @@ const useChats = (search?: string) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = flattenPages(queryInfo);
|
const data = flattenPages(queryInfo.data);
|
||||||
|
|
||||||
const chatsQuery = {
|
const chatsQuery = {
|
||||||
...queryInfo,
|
...queryInfo,
|
||||||
|
@ -171,7 +171,7 @@ const useChat = (chatId?: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const chat = useQuery<IChat | undefined>(chatKeys.chat(chatId), getChat);
|
const chat = useQuery<IChat | undefined>(ChatKeys.chat(chatId), getChat);
|
||||||
|
|
||||||
return { ...actions, chat };
|
return { ...actions, chat };
|
||||||
};
|
};
|
||||||
|
@ -195,8 +195,8 @@ const useChatActions = (chatId: string) => {
|
||||||
const acceptChat = useMutation(() => api.post<IChat>(`/api/v1/pleroma/chats/${chatId}/accept`), {
|
const acceptChat = useMutation(() => api.post<IChat>(`/api/v1/pleroma/chats/${chatId}/accept`), {
|
||||||
onSuccess(response) {
|
onSuccess(response) {
|
||||||
setChat(response.data);
|
setChat(response.data);
|
||||||
queryClient.invalidateQueries(chatKeys.chatMessages(chatId));
|
queryClient.invalidateQueries(ChatKeys.chatMessages(chatId));
|
||||||
queryClient.invalidateQueries(chatKeys.chatSearch());
|
queryClient.invalidateQueries(ChatKeys.chatSearch());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -204,8 +204,8 @@ const useChatActions = (chatId: string) => {
|
||||||
onSuccess(response) {
|
onSuccess(response) {
|
||||||
setChat(null);
|
setChat(null);
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
queryClient.invalidateQueries(chatKeys.chatMessages(chatId));
|
queryClient.invalidateQueries(ChatKeys.chatMessages(chatId));
|
||||||
queryClient.invalidateQueries(chatKeys.chatSearch());
|
queryClient.invalidateQueries(ChatKeys.chatSearch());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ const useChatSilences = () => {
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
return useQuery<IChatSilence[]>(chatKeys.chatSilences, getChatSilences, {
|
return useQuery<IChatSilence[]>(ChatKeys.chatSilences, getChatSilences, {
|
||||||
placeholderData: [],
|
placeholderData: [],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -260,7 +260,7 @@ const useChatSilence = (chat: IChat | null) => {
|
||||||
api.post(`api/v1/pleroma/chats/silence?account_id=${chat?.account.id}`)
|
api.post(`api/v1/pleroma/chats/silence?account_id=${chat?.account.id}`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch(snackbar.success('Successfully silenced this chat.'));
|
dispatch(snackbar.success('Successfully silenced this chat.'));
|
||||||
queryClient.invalidateQueries(chatKeys.chatSilences);
|
queryClient.invalidateQueries(ChatKeys.chatSilences);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
dispatch(snackbar.error('Something went wrong trying to silence this chat. Please try again.'));
|
dispatch(snackbar.error('Something went wrong trying to silence this chat. Please try again.'));
|
||||||
|
@ -274,7 +274,7 @@ const useChatSilence = (chat: IChat | null) => {
|
||||||
api.delete(`api/v1/pleroma/chats/silence?account_id=${chat?.account.id}`)
|
api.delete(`api/v1/pleroma/chats/silence?account_id=${chat?.account.id}`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch(snackbar.success('Successfully unsilenced this chat.'));
|
dispatch(snackbar.success('Successfully unsilenced this chat.'));
|
||||||
queryClient.invalidateQueries(chatKeys.chatSilences);
|
queryClient.invalidateQueries(ChatKeys.chatSilences);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
dispatch(snackbar.error('Something went wrong trying to unsilence this chat. Please try again.'));
|
dispatch(snackbar.error('Something went wrong trying to unsilence this chat. Please try again.'));
|
||||||
|
@ -285,4 +285,4 @@ const useChatSilence = (chat: IChat | null) => {
|
||||||
return { isSilenced, handleSilence, fetchChatSilence };
|
return { isSilenced, handleSilence, fetchChatSilence };
|
||||||
};
|
};
|
||||||
|
|
||||||
export { chatKeys, useChat, useChatActions, useChats, useChatMessages, useChatSilences, useChatSilence };
|
export { ChatKeys, useChat, useChatActions, useChats, useChatMessages, useChatSilences, useChatSilence };
|
||||||
|
|
|
@ -32,7 +32,7 @@ type PageParam = {
|
||||||
link?: string
|
link?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const suggestionKeys = {
|
const SuggestionKeys = {
|
||||||
suggestions: ['suggestions'] as const,
|
suggestions: ['suggestions'] as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ const useSuggestions = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = useInfiniteQuery(
|
const result = useInfiniteQuery(
|
||||||
suggestionKeys.suggestions,
|
SuggestionKeys.suggestions,
|
||||||
({ pageParam }: any) => getSuggestions(pageParam),
|
({ pageParam }: any) => getSuggestions(pageParam),
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
|
@ -123,7 +123,7 @@ const useDismissSuggestion = () => {
|
||||||
|
|
||||||
return useMutation((accountId: string) => api.delete(`/api/v1/suggestions/${accountId}`), {
|
return useMutation((accountId: string) => api.delete(`/api/v1/suggestions/${accountId}`), {
|
||||||
onMutate(accountId: string) {
|
onMutate(accountId: string) {
|
||||||
removePageItem(suggestionKeys.suggestions, accountId, (o: any, n: any) => o.account_id === n);
|
removePageItem(SuggestionKeys.suggestions, accountId, (o: any, n: any) => o.account_id === n);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,8 +9,8 @@ export interface PaginatedResult<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Flatten paginated results into a single array. */
|
/** Flatten paginated results into a single array. */
|
||||||
const flattenPages = <T>(queryInfo: UseInfiniteQueryResult<PaginatedResult<T>>) => {
|
const flattenPages = <T>(queryData: UseInfiniteQueryResult<PaginatedResult<T>>['data']) => {
|
||||||
return queryInfo.data?.pages.reduce<T[]>(
|
return queryData?.pages.reduce<T[]>(
|
||||||
(prev: T[], curr) => [...curr.result, ...prev],
|
(prev: T[], curr) => [...curr.result, ...prev],
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue