Untitled

 avatar
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