From ca3057c895102a94496f36d794a58296ccc7983c Mon Sep 17 00:00:00 2001 From: Bob Burningham Date: Sun, 29 Oct 2023 17:23:13 -0700 Subject: [PATCH] added chat scroll hook --- components/chat/chat-messages.tsx | 45 ++++++++++++++++++----- hooks/use-chat-scroll.ts | 60 +++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 hooks/use-chat-scroll.ts diff --git a/components/chat/chat-messages.tsx b/components/chat/chat-messages.tsx index a655b7e..84f3c54 100644 --- a/components/chat/chat-messages.tsx +++ b/components/chat/chat-messages.tsx @@ -1,14 +1,15 @@ "use client"; -import { Member, Message, Profile } from "@prisma/client"; +import { Fragment, useRef, ElementRef, use } from "react"; import { format } from "date-fns" +import { Member, Message, Profile } from "@prisma/client" import { ChatWelcome } from "./chat-welcome"; import { useChatQuery } from "@/hooks/use-chat-query"; import { Loader2, ServerCrash } from "lucide-react"; -import { Fragment } from "react"; import { ChatItem } from "./chat-item"; import { useChatSocket } from "@/hooks/use-chat-socket"; +import { useChatScroll } from "@/hooks/use-chat-scroll"; const DATE_FORMAT = "d MMM yyyy, HH:mm"; @@ -45,6 +46,9 @@ export const ChatMessages = ({ const addKey = `chat:${chatId}:messages`; const updateKey = `chat:${chatId}:messages:update`; + const chatRef = useRef>(null); + const bottomRef = useRef>(null); + const { data, fetchNextPage, @@ -58,6 +62,14 @@ export const ChatMessages = ({ paramValue, }); useChatSocket({ queryKey, addKey, updateKey}); + useChatScroll({ + chatRef, + bottomRef, + loadMore: fetchNextPage, + shouldLoadMore: !isFetchingNextPage && !!hasNextPage, + count: data?.pages?.[0]?.items?.length ?? 0, + + }) if (status === "pending") { return ( @@ -82,12 +94,28 @@ export const ChatMessages = ({ } return ( -
-
- +
+ {!hasNextPage &&
} + {!hasNextPage &&( + + )} + {hasNextPage && ( +
+ {isFetchingNextPage ? ( + + ): ( + + )} +
+ )}
{data?.pages.map((group, i) => ( @@ -110,6 +138,7 @@ export const ChatMessages = ({ ))}
+
) } \ No newline at end of file diff --git a/hooks/use-chat-scroll.ts b/hooks/use-chat-scroll.ts new file mode 100644 index 0000000..7286088 --- /dev/null +++ b/hooks/use-chat-scroll.ts @@ -0,0 +1,60 @@ +import { useEffect, useState } from "react"; + +type ChatScrollProps = { + chatRef: React.RefObject; + bottomRef: React.RefObject; + shouldLoadMore: boolean; + loadMore: () => void; + count: number; +} + +export const useChatScroll = ({ + chatRef, + bottomRef, + shouldLoadMore, + loadMore, + count, +}: ChatScrollProps) => { + const [hasInitialized, setHasInitialized] = useState(false); + + useEffect(() => { + const topDiv = chatRef?.current; + const hadleScroll = () => { + const scrollTop = topDiv?.scrollTop; + + if (scrollTop === 0 && shouldLoadMore) { + loadMore(); + } + }; + + topDiv?.addEventListener("scroll", hadleScroll); + + return () => { + topDiv?.removeEventListener("scroll", hadleScroll); + } + }, [shouldLoadMore, loadMore, chatRef]); + + useEffect(() => { + const bottomDiv = bottomRef?.current; + const topDiv = chatRef?.current; + const shouldAutoScroll= () => { + if (!hasInitialized && bottomDiv){ + setHasInitialized(true); + return true; + } + + if (!topDiv) { + return false; + } + + const distanceFromBottom = topDiv.scrollHeight - topDiv.clientHeight; + return distanceFromBottom <= 100; + } + + if (shouldAutoScroll()) { + setTimeout(() => { + bottomRef.current?.scrollIntoView({ behavior: "smooth" }); + }, 100); + } + }, [bottomRef, chatRef, hasInitialized, count]); +} \ No newline at end of file