discord-clone/components/chat/chat-messages.tsx

144 lines
4.7 KiB
TypeScript
Raw Normal View History

"use client";
2023-10-29 17:23:13 -07:00
import { Fragment, useRef, ElementRef, use } from "react";
import { format } from "date-fns"
2023-10-29 17:23:13 -07:00
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 { ChatItem } from "./chat-item";
import { useChatSocket } from "@/hooks/use-chat-socket";
2023-10-29 17:23:13 -07:00
import { useChatScroll } from "@/hooks/use-chat-scroll";
const DATE_FORMAT = "d MMM yyyy, HH:mm";
type MessageWithMemberWithProfile = Message & {
member: Member & {
profile: Profile
}
}
interface ChatMessagesProps {
name: string;
member: Member;
chatId: string;
apiUrl: string;
socketUrl: string;
socketQuery: Record<string, string>;
paramKey: "channelId" | "conversationId";
paramValue: string;
type: "channel" | "conversation";
}
export const ChatMessages = ({
name,
member,
chatId,
apiUrl,
socketUrl,
socketQuery,
paramKey,
paramValue,
type,
}: ChatMessagesProps) => {
const queryKey = `chat:${chatId}`;
const addKey = `chat:${chatId}:messages`;
const updateKey = `chat:${chatId}:messages:update`;
2023-10-29 17:23:13 -07:00
const chatRef = useRef<ElementRef<"div">>(null);
const bottomRef = useRef<ElementRef<"div">>(null);
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
status,
} = useChatQuery({
queryKey,
apiUrl,
paramKey,
paramValue,
});
useChatSocket({ queryKey, addKey, updateKey});
2023-10-29 17:23:13 -07:00
useChatScroll({
chatRef,
bottomRef,
loadMore: fetchNextPage,
shouldLoadMore: !isFetchingNextPage && !!hasNextPage,
count: data?.pages?.[0]?.items?.length ?? 0,
})
if (status === "pending") {
return (
<div className="flex flex-col flex-1 justify-center items-center">
<Loader2 className="h-7 w-7 text-zinc-500 animate-spin my-4"/>
<p className="text-xs text-zinc-500 dark:text-zinc-400">
Loading Messages...
</p>
</div>
)
}
if (status === "error") {
return (
<div className="flex flex-col flex-1 justify-center items-center">
<ServerCrash className="h-7 w-7 text-zinc-500 my-4"/>
<p className="text-xs text-zinc-500 dark:text-zinc-400">
Something went wrong!
</p>
</div>
)
}
return (
2023-10-29 17:23:13 -07:00
<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}>
{group.items.map((message: MessageWithMemberWithProfile) => (
<ChatItem
key={message.id}
id={message.id}
currentMember={member}
member={message.member}
content={message.content}
fileUrl={message.fileUrl}
deleted={message.deleted}
timestamp={format(new Date(message.createdAt), DATE_FORMAT)}
isUpdated={message.updatedAt !== message.createdAt}
socketUrl={socketUrl}
socketQuery={socketQuery}
/>
))}
</Fragment>
))}
</div>
2023-10-29 17:23:13 -07:00
<div ref={bottomRef}/>
</div>
)
}