added chat scroll hook
This commit is contained in:
parent
a105e6180e
commit
ca3057c895
@ -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"/>
|
||||
<ChatWelcome
|
||||
type={type}
|
||||
name={name}
|
||||
/>
|
||||
<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
60
hooks/use-chat-scroll.ts
Normal 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]);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user