76 lines
2.1 KiB
JavaScript
76 lines
2.1 KiB
JavaScript
/**
|
|
* InfiniteScrollMessages Component
|
|
* Wrapper around MessageList that implements infinite scroll
|
|
* Detects when user scrolls to top and loads more messages
|
|
*/
|
|
|
|
import React, { useEffect, useRef, useCallback } from 'react';
|
|
import MessageList from './MessageList';
|
|
import './InfiniteScrollMessages.css';
|
|
|
|
export default function InfiniteScrollMessages({
|
|
messages,
|
|
typingUsers,
|
|
onLoadMore,
|
|
hasMore = true,
|
|
isLoading = false,
|
|
threshold = 300, // Pixels from top to trigger load
|
|
}) {
|
|
const scrollContainerRef = useRef(null);
|
|
const sentinelRef = useRef(null);
|
|
const intersectionObserverRef = useRef(null);
|
|
|
|
// Intersection Observer for infinite scroll detection
|
|
useEffect(() => {
|
|
if (!hasMore || isLoading) return;
|
|
|
|
const options = {
|
|
root: scrollContainerRef.current,
|
|
rootMargin: `${threshold}px 0px 0px 0px`,
|
|
threshold: 0.01,
|
|
};
|
|
|
|
intersectionObserverRef.current = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting && hasMore && !isLoading) {
|
|
onLoadMore?.();
|
|
}
|
|
});
|
|
}, options);
|
|
|
|
if (sentinelRef.current) {
|
|
intersectionObserverRef.current.observe(sentinelRef.current);
|
|
}
|
|
|
|
return () => {
|
|
if (intersectionObserverRef.current) {
|
|
intersectionObserverRef.current.disconnect();
|
|
}
|
|
};
|
|
}, [hasMore, isLoading, onLoadMore, threshold]);
|
|
|
|
return (
|
|
<div className="infinite-scroll-messages-container" ref={scrollContainerRef}>
|
|
{/* Loading indicator at top */}
|
|
{isLoading && (
|
|
<div className="scroll-loading-indicator">
|
|
<div className="spinner"></div>
|
|
<span>Loading messages...</span>
|
|
</div>
|
|
)}
|
|
|
|
{/* Sentinel element - triggers load when visible */}
|
|
<div ref={sentinelRef} className="scroll-sentinel" />
|
|
|
|
{/* Messages */}
|
|
<MessageList messages={messages} typingUsers={typingUsers} />
|
|
|
|
{/* End of messages indicator */}
|
|
{!hasMore && messages.length > 0 && (
|
|
<div className="scroll-end-indicator">
|
|
<span>Beginning of conversation</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|