2024-08-03 19:14:38 +02:00
|
|
|
import React from 'react';
|
|
|
|
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
|
|
|
import { InView } from 'react-intersection-observer';
|
|
|
|
|
import { Message } from '@/components/messages/message';
|
2024-08-22 20:17:36 +02:00
|
|
|
import { MessageNode } from '@/types/messages';
|
2024-08-03 19:14:38 +02:00
|
|
|
|
|
|
|
|
interface MessageListProps {
|
2024-08-06 21:45:12 +02:00
|
|
|
conversationId: string;
|
2024-08-22 20:17:36 +02:00
|
|
|
messages: MessageNode[];
|
2024-08-03 19:14:38 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-22 20:17:36 +02:00
|
|
|
export const MessageList = ({ conversationId, messages }: MessageListProps) => {
|
2024-08-03 19:14:38 +02:00
|
|
|
const viewportRef = React.useRef<HTMLDivElement>(null);
|
|
|
|
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
|
|
|
const observerRef = React.useRef<ResizeObserver | null>(null);
|
|
|
|
|
const scrollPosition = React.useRef<number>(0);
|
|
|
|
|
const bottomRef = React.useRef<HTMLDivElement>(null);
|
|
|
|
|
const bottomVisibleRef = React.useRef<boolean>(false);
|
|
|
|
|
const shouldScrollToBottom = React.useRef<boolean>(true);
|
|
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
if (bottomRef.current && scrollPosition.current == 0) {
|
|
|
|
|
bottomRef.current.scrollIntoView();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (containerRef.current && viewportRef.current) {
|
|
|
|
|
// observe resize of container when new messages are appended or internal elements are loaded (e.g. images)
|
|
|
|
|
observerRef.current = new ResizeObserver(() => {
|
|
|
|
|
if (!viewportRef.current) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shouldScrollToBottom) {
|
|
|
|
|
bottomRef.current?.scrollIntoView();
|
|
|
|
|
} else {
|
|
|
|
|
viewportRef.current.scrollTop =
|
|
|
|
|
viewportRef.current.scrollHeight - scrollPosition.current;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
observerRef.current.observe(containerRef.current);
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
if (observerRef.current) {
|
|
|
|
|
observerRef.current.disconnect();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2024-08-06 21:45:12 +02:00
|
|
|
}, [conversationId]);
|
2024-08-03 19:14:38 +02:00
|
|
|
|
|
|
|
|
const handleScroll = () => {
|
|
|
|
|
if (viewportRef.current) {
|
|
|
|
|
scrollPosition.current =
|
|
|
|
|
viewportRef.current.scrollHeight - viewportRef.current.scrollTop;
|
|
|
|
|
|
|
|
|
|
shouldScrollToBottom.current = bottomVisibleRef.current;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<ScrollArea
|
|
|
|
|
ref={viewportRef}
|
|
|
|
|
onScroll={handleScroll}
|
|
|
|
|
className="flex-grow overflow-y-auto px-10"
|
|
|
|
|
>
|
|
|
|
|
<InView
|
|
|
|
|
rootMargin="200px"
|
|
|
|
|
onChange={(inView) => {
|
|
|
|
|
// if (inView && hasPrevious && !isLoadingPrevious) {
|
|
|
|
|
// loadPrevious(50);
|
|
|
|
|
// }
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{/*<div className="flex w-full items-center justify-center py-4">*/}
|
|
|
|
|
{/* {isLoadingPrevious && <Loader />}*/}
|
|
|
|
|
{/*</div>*/}
|
|
|
|
|
</InView>
|
2024-08-29 19:40:36 +02:00
|
|
|
<div className="container" ref={containerRef}>
|
2024-08-03 19:14:38 +02:00
|
|
|
{messages.map((message, index) => {
|
|
|
|
|
const previousMessage = index > 0 ? messages[index - 1] : null;
|
|
|
|
|
|
|
|
|
|
const currentMessageDate = new Date(message.createdAt);
|
|
|
|
|
const previousMessageDate = previousMessage
|
|
|
|
|
? new Date(previousMessage.createdAt)
|
|
|
|
|
: null;
|
|
|
|
|
const showDate =
|
|
|
|
|
!previousMessageDate ||
|
|
|
|
|
currentMessageDate.getDate() !== previousMessageDate.getDate();
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<React.Fragment key={message.id}>
|
|
|
|
|
{showDate && (
|
|
|
|
|
<div className="relative flex items-center py-1">
|
|
|
|
|
<div className="flex-grow border-t border-gray-100" />
|
|
|
|
|
<span className="mx-4 flex-shrink text-xs text-muted-foreground">
|
|
|
|
|
{currentMessageDate.toDateString()}
|
|
|
|
|
</span>
|
|
|
|
|
<div className="flex-grow border-t border-gray-100" />
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2024-08-22 20:17:36 +02:00
|
|
|
<Message message={message} previousMessage={previousMessage} />
|
2024-08-03 19:14:38 +02:00
|
|
|
</React.Fragment>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
<InView
|
|
|
|
|
className="h-4"
|
|
|
|
|
rootMargin="20px"
|
|
|
|
|
onChange={(inView) => {
|
|
|
|
|
bottomVisibleRef.current = inView;
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div ref={bottomRef} className="h-4"></div>
|
|
|
|
|
</InView>
|
|
|
|
|
</ScrollArea>
|
|
|
|
|
);
|
|
|
|
|
};
|