Skip to content

Commit eb38ea8

Browse files
committed
Refactor scroll
1 parent d40f778 commit eb38ea8

File tree

1 file changed

+26
-48
lines changed

1 file changed

+26
-48
lines changed

chat/src/components/MessageList.tsx

Lines changed: 26 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"use client";
22

3-
import { useEffect, useRef } from "react";
4-
53
interface Message {
64
role: string;
75
content: string;
@@ -17,49 +15,6 @@ export default function MessageList({
1715
messages,
1816
loading = false,
1917
}: MessageListProps) {
20-
const messagesEndRef = useRef<HTMLDivElement>(null);
21-
const prevMessagesLengthRef = useRef<number>(0);
22-
const prevLoadingRef = useRef<boolean>(false);
23-
24-
// Enhanced scrolling behavior to handle:
25-
// 1. New messages being added
26-
// 2. Loading indicator appearing/disappearing
27-
// 3. New user messages (to ensure they're always visible)
28-
useEffect(() => {
29-
const lastMessage = messages[messages.length - 1];
30-
const isNewUserMessage =
31-
messages.length > prevMessagesLengthRef.current &&
32-
lastMessage?.role === "user";
33-
34-
const loadingChanged = loading !== prevLoadingRef.current;
35-
36-
// Store current scroll position and total scroll height
37-
const messageContainer = messagesEndRef.current?.parentElement;
38-
if (messagesEndRef.current && messageContainer) {
39-
// Determine if we should force scroll
40-
const shouldForceScroll =
41-
isNewUserMessage || // New user message added
42-
loading || // Loading indicator is active
43-
loadingChanged; // Loading state changed
44-
45-
// Check if we're already near the bottom
46-
const isNearBottom =
47-
messageContainer.scrollHeight -
48-
messageContainer.scrollTop -
49-
messageContainer.clientHeight <
50-
100;
51-
52-
// Scroll if we're forced to or if we're already near the bottom
53-
if (shouldForceScroll || isNearBottom) {
54-
messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
55-
}
56-
}
57-
58-
// Update references for next comparison
59-
prevMessagesLengthRef.current = messages.length;
60-
prevLoadingRef.current = loading;
61-
}, [messages, loading]);
62-
6318
// If no messages, show a placeholder
6419
if (messages.length === 0) {
6520
return (
@@ -89,7 +44,32 @@ export default function MessageList({
8944
);
9045

9146
return (
92-
<div className="overflow-y-auto">
47+
<div
48+
className="overflow-y-auto"
49+
ref={(scrollAreaRef) => {
50+
if (!scrollAreaRef) {
51+
return;
52+
}
53+
54+
scrollAreaRef.scrollTo({
55+
top: scrollAreaRef.scrollHeight,
56+
});
57+
58+
const callback: MutationCallback = (mutationsList) => {
59+
for (const mutation of mutationsList) {
60+
if (mutation.type === "childList") {
61+
scrollAreaRef.scrollTo({
62+
top: scrollAreaRef.scrollHeight,
63+
behavior: "smooth",
64+
});
65+
}
66+
}
67+
};
68+
69+
const observer = new MutationObserver(callback);
70+
observer.observe(scrollAreaRef, { childList: true, subtree: false });
71+
}}
72+
>
9373
<div className="p-4 flex flex-col gap-4 max-w-4xl mx-auto">
9474
{messages.map((message) => (
9575
<div
@@ -124,8 +104,6 @@ export default function MessageList({
124104
<LoadingDots />
125105
</div>
126106
)}
127-
128-
<div ref={messagesEndRef} />
129107
</div>
130108
</div>
131109
);

0 commit comments

Comments
 (0)