2023-10-27 22:45:31 -07:00
|
|
|
"use client";
|
|
|
|
|
2023-10-29 17:23:13 -07:00
|
|
|
import { Fragment, useRef, ElementRef, use } from "react";
|
2023-10-28 00:31:56 -07:00
|
|
|
import { format } from "date-fns"
|
2023-10-29 17:23:13 -07:00
|
|
|
import { Member, Message, Profile } from "@prisma/client"
|
2023-10-27 22:45:31 -07:00
|
|
|
|
|
|
|
import { ChatWelcome } from "./chat-welcome";
|
|
|
|
import { useChatQuery } from "@/hooks/use-chat-query";
|
|
|
|
import { Loader2, ServerCrash } from "lucide-react";
|
2023-10-28 00:31:56 -07:00
|
|
|
import { ChatItem } from "./chat-item";
|
2023-10-29 16:28:03 -07:00
|
|
|
import { useChatSocket } from "@/hooks/use-chat-socket";
|
2023-10-29 17:23:13 -07:00
|
|
|
import { useChatScroll } from "@/hooks/use-chat-scroll";
|
2023-10-28 00:31:56 -07:00
|
|
|
|
|
|
|
const DATE_FORMAT = "d MMM yyyy, HH:mm";
|
2023-10-27 22:45:31 -07:00
|
|
|
|
|
|
|
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}`;
|
2023-10-29 16:28:03 -07:00
|
|
|
const addKey = `chat:${chatId}:messages`;
|
|
|
|
const updateKey = `chat:${chatId}:messages:update`;
|
2023-10-27 22:45:31 -07:00
|
|
|
|
2023-10-29 17:23:13 -07:00
|
|
|
const chatRef = useRef<ElementRef<"div">>(null);
|
|
|
|
const bottomRef = useRef<ElementRef<"div">>(null);
|
|
|
|
|
2023-10-27 22:45:31 -07:00
|
|
|
const {
|
|
|
|
data,
|
|
|
|
fetchNextPage,
|
|
|
|
hasNextPage,
|
|
|
|
isFetchingNextPage,
|
|
|
|
status,
|
|
|
|
} = useChatQuery({
|
|
|
|
queryKey,
|
|
|
|
apiUrl,
|
|
|
|
paramKey,
|
|
|
|
paramValue,
|
2023-10-29 16:28:03 -07:00
|
|
|
});
|
|
|
|
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,
|
|
|
|
|
|
|
|
})
|
2023-10-27 22:45:31 -07:00
|
|
|
|
|
|
|
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>
|
|
|
|
)}
|
2023-10-27 22:45:31 -07:00
|
|
|
<div className="flex flex-col-reverse mt-auto">
|
|
|
|
{data?.pages.map((group, i) => (
|
|
|
|
<Fragment key={i}>
|
|
|
|
{group.items.map((message: MessageWithMemberWithProfile) => (
|
2023-10-28 00:31:56 -07:00
|
|
|
<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}
|
|
|
|
|
|
|
|
/>
|
2023-10-27 22:45:31 -07:00
|
|
|
))}
|
|
|
|
</Fragment>
|
|
|
|
))}
|
|
|
|
</div>
|
2023-10-29 17:23:13 -07:00
|
|
|
<div ref={bottomRef}/>
|
2023-10-27 22:45:31 -07:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|