Untitled
unknown
plain_text
a year ago
8.8 kB
10
Indexable
"use client"
import React from 'react'
import {
QueryClient,
QueryClientProvider,
} from 'react-query'
import { persistQueryClient } from 'react-query/persistQueryClient-experimental'
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'
const queryProvider = ({ children }: { children: React.ReactNode }) => {
const [queryClient] = React.useState(() => new QueryClient(
{
defaultOptions: {
queries: {
cacheTime: 1000 * *60 * *60 * 48, // 2 days in milliseconds
staleTime: 1000 * *60 * *60 * 48,
}
}
}
));
return (
<QueryClientProvider client={queryClient} >
{children}
</QueryClientProvider>
)
}
export default React.memo(queryProvider);
My query provider, i wrapped it around the whole app lay.out.tsx
const { data: chatQueryData, hasNextPage, isFetchingNextPage, status, fetchNextPage } = useChatQuery({
api: !directConversation ? "/api/Messages/GetMessages" : '/api/Messages/directMessages',
query: queryKey,
paramKey: !directConversation ? "channelId" : "conversationId",
paramValue: channelId
});
//issue in servers part only
const messages = useMemo(() => {
if (!chatQueryData || !chatQueryData.pages) return [];
return chatQueryData.pages.flatMap((page) => page?.items || []);
}, [chatQueryData]); // Dependencies array should be empty if useChatQuery is not dependent on external props or state
const [optimisticMessages, addOptimisticMessage, resetOptimisticMessages] = useOptimistic<ChatMessageType[], Partial<ChatMessageType>>(
messages,
(state, newMessage) => [
{
...newMessage as ChatMessageType,
pending: true,
}
, ...state]
);
useChatSocket({ //adds message, or updates it when it on socket
queryKey: queryKey,
addKey,
updateKey,
onNewMessage: (newMessage) => {
resetOptimisticMessages(newMessage)
}
})
// from chat componet resposbile for showing/rendering/sending messages,
it works properly,
useReactionSocket({ //adds a reaction or update.
addReactionKey: chat://${channelId}:reactions,
updateReactionKey: chat://${channelId}:reactions:update,
queryKey
});
useScroll({ //scroll loads
chatRef,
bottomRef,
loadMore: fetchNextPage,
Load: !isFetchingNextPage && !!hasNextPage,
count: chatQueryData?.pages[0]?.length ?? 0,
scrollRef
})
//extra hooks in the same chat app,
export const useQuery = ({ query, api, paramKey, paramValue }) => {
const { isConnected } = useSocket();
const fetchMessage = async ({ pageParam = null }) => {
const url = qs.stringifyUrl({
url: api,
query: {
cursor: pageParam,
[paramKey]: paramValue
}
}, { skipNull: true })
const res = await fetch(url, { next: { revalidate: 3600 } });
return res.json();
}
const { data, hasNextPage, isFetchingNextPage, status, fetchNextPage } = useInfiniteQuery({
queryKey: query,
queryFn: fetchMessage,
getNextPageParam: (PrevPage) => PrevPage?.nextCursor,
refetchInterval: () => isConnected ? 10000 : false,
staleTime: 100,
});
return { data, hasNextPage, isFetchingNextPage, status, fetchNextPage }
//useChatQuery
import { useSocket } from '@/providers/socketProvider';
import { Member, Message, Profile } from '@prisma/client';
import React, { useEffect } from 'react'
import { useQueryClient } from 'react-query';
type ChatSocketProps = {
addKey: string;
updateKey: string;
queryKey: string[];
onNewMessage: (newMessage: any) => void
}
type MessageWithMemberProfile = Message & {
member: Member & {
profile: Profile
}
}
const useChatSocket = ({ addKey, updateKey, queryKey, onNewMessage }: ChatSocketProps) => {
const { socket } = useSocket();
const queryClient = useQueryClient();
useEffect(() => {
if (!socket) return;
/socket.on(getUsers, (data received) => return data ) on the front end, i use it to filter, if it exists
then i just give them green circle/
//uhhh
socket.on(updateKey, (message: MessageWithMemberProfile) => {
queryClient.setQueryData([queryKey], (oldData: any) => {
if (!oldData || !oldData.pages || oldData?.pages?.length === 0) {
const newData = oldData?.pages?.map((page: any) => ({
...page,
items: page.items.map((item: MessageWithMemberProfile) =>
item.id === message.id ? message : item
)
}));
return newData;
}
})
})
socket.on(addKey, (message: MessageWithMemberProfile) => {
queryClient.setQueryData([queryKey], (oldData: any) => {
if (!oldData || !oldData?.pages || oldData?.pages?.length === 0) {
return {
pages: [{
items: [message],
}]
}
}
const newData = [...oldData.pages];
newData[0] = {
...newData[0],
items: [{
message,
...newData[0].items
}]
}
const updatedPages = newData.map(page => ({
...page,
items: page.items.filter((item: MessageWithMemberProfile & {
pending: boolean
}) =>
!item.pending || item.content !== message.content // Check for content match to remove the optimistic message
)
}));
return {
...oldData,
pages: updatedPages
}
});
if (onNewMessage) {
onNewMessage(message);
}
})
return () => {
socket.off(addKey);
socket.off(updateKey);
}
}, [queryClient, addKey, updateKey, socket, queryKey, onNewMessage])
}
//useChatSocket
//my custom useOptimistic (nextJS doesnt use reactjs useOptimistic properly)
export default function useOptimistic<T, P>(passthrough: T, reducer: (state: T, payload: P) => T) {
const [value, setValue] = useState(passthrough);
const historyRef = useRef<T[]>([]);
useEffect(() => {
setValue(passthrough);
historyRef.current = [passthrough];
}, [passthrough]);
const reducerRef = useRef(reducer);
useLayoutEffect(() => {
reducerRef.current = reducer;
}, []);
const dispatch = useCallback((payload: P) => {
setValue((prevValue) => {
const newState = reducerRef.current(prevValue, payload);
historyRef.current.push(newState);
return newState;
});
}, []);
const reset = useCallback((newMessage: Partial<T[any]>) => {
setValue((prevValue) => {
const filteredValue = prevValue?.filter((msg: any) =>
msg.id !== newMessage.id || !msg.pending
);
historyRef.current = [filteredValue];
return filteredValue;
});
}, []);
return [value, dispatch, reset] as const;
}
const useReactionSocket = ({ addReactionKey, updateReactionKey, queryKey }: ReactionSocketProps) => {
const { socket } = useSocket();
const queryClient = useQueryClient();
useEffect(() => {
if (!socket) return;
// Handle new reactions
socket.on(addReactionKey, (reaction: ReactionWithMember) => {
queryClient.setQueryData([queryKey], (oldData: any) => {
if (!oldData || !oldData.pages || oldData?.pages?.length === 0) {
return oldData;
}
const newData = oldData.pages.map((page: any) => ({
...page,
items: page.items.map((item: any) => {
if (item.id === reaction.messageId || item.id === reaction.directMessageId) {
return {
...item,
reactions: [...item.reactions, reaction]
};
}
return item;
})
}));
return { ...oldData, pages: newData };
});
});
// Handle reaction updates (e.g., removing a reaction)
socket.on(updateReactionKey, (reaction: ReactionWithMember) => {
queryClient.setQueryData([queryKey], (oldData: any) => {
if (!oldData || !oldData.pages || oldData?.pages?.length === 0) {
return oldData;
}
const newData = oldData.pages.map((page: any) => ({
...page,
items: page.items.map((item: any) => {
if (item.id === reaction.messageId || item.id === reaction.directMessageId) {
return {
...item,
reactions: item.reactions.map((r: ReactionWithMember) =>
r.id === reaction.id ? reaction : r
)
};
}
return item;
})
}));
return { ...oldData, pages: newData };
});
});
return () => {
socket.off(addReactionKey);
socket.off(updateReactionKey);
};
}, [queryClient, addReactionKey, updateReactionKey, socket, queryKey]);
};Editor is loading...
Leave a Comment