added chat scroll hook

This commit is contained in:
Bob Burningham 2023-10-29 17:23:13 -07:00
parent a105e6180e
commit ca3057c895
2 changed files with 97 additions and 8 deletions

View File

@ -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<ElementRef<"div">>(null);
const bottomRef = useRef<ElementRef<"div">>(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 (
<div className="flex-1 flex flex-col py-4 overflow-y-auto">
<div className="flex-1"/>
<div ref={chatRef} className="flex-1 flex flex-col py-4 overflow-y-auto">
{!hasNextPage && <div className="flex-1"/>}
{!hasNextPage &&(
<ChatWelcome
type={type}
name={name}
/>
)}
{hasNextPage && (
<div className="flex justify-center">
{isFetchingNextPage ? (
<Loader2 className="h-6 w-6 text-zinc-500 animate-spin my-4"/>
): (
<button
onClick={() => fetchNextPage()}
className="text-zinc-500 hover:text-zinc-600 dark:text-zinc-400 text-xs dark:hover:text-zinc-300 transition"
>
Load previous messages
</button>
)}
</div>
)}
<div className="flex flex-col-reverse mt-auto">
{data?.pages.map((group, i) => (
<Fragment key={i}>
@ -110,6 +138,7 @@ export const ChatMessages = ({
</Fragment>
))}
</div>
<div ref={bottomRef}/>
</div>
)
}

60
hooks/use-chat-scroll.ts Normal file
View File

@ -0,0 +1,60 @@
import { useEffect, useState } from "react";
type ChatScrollProps = {
chatRef: React.RefObject<HTMLDivElement>;
bottomRef: React.RefObject<HTMLDivElement>;
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]);
}