Untitled
unknown
plain_text
10 months ago
8.8 kB
6
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