From cca6ea0e372ee7cb15e63cc6a0f61c658dcb1113 Mon Sep 17 00:00:00 2001 From: hobokenchicken Date: Tue, 21 Jul 2026 09:05:39 -0400 Subject: [PATCH] fix: stop infinite scroll feedback when no more messages Scroll handler now checks !hasMore to avoid calling fetchOlderMessages when all messages are loaded. Previously the handler would fire on every scroll event (since scrollTop stayed < 100), creating a .then() callback loop that adjusted scroll position repeatedly, causing 'stuck' scrolling. --- web/src/components/ChatArea.tsx | 6 +++--- web/src/components/DMChat.tsx | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/web/src/components/ChatArea.tsx b/web/src/components/ChatArea.tsx index 679910a..914d69a 100644 --- a/web/src/components/ChatArea.tsx +++ b/web/src/components/ChatArea.tsx @@ -292,6 +292,7 @@ export function ChatArea() { ); const isLoading = useMessageStore((s) => s.isLoading); const isLoadingOlder = useMessageStore((s) => s.isLoadingOlder); + const hasMore = useMessageStore((s) => activeChannelId ? s.hasMoreByChannel[activeChannelId] !== false : true); const fetchMessages = useMessageStore((s) => s.fetchMessages); const fetchOlderMessages = useMessageStore((s) => s.fetchOlderMessages); const sendMessage = useMessageStore((s) => s.sendMessage); @@ -447,17 +448,16 @@ export function ChatArea() { const handleScroll = useCallback(() => { const el = scrollContainerRef.current; - if (!el || !activeChannelId || isLoadingOlder) return; + if (!el || !activeChannelId || isLoadingOlder || !hasMore) return; if (el.scrollTop < 100) { const prevHeight = el.scrollHeight; fetchOlderMessages(activeChannelId).then(() => { - // ponytail: maintain scroll position after prepending older messages requestAnimationFrame(() => { el.scrollTop = el.scrollHeight - prevHeight; }); }); } - }, [activeChannelId, isLoadingOlder, fetchOlderMessages]); + }, [activeChannelId, isLoadingOlder, hasMore, fetchOlderMessages]); useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: "auto" }); diff --git a/web/src/components/DMChat.tsx b/web/src/components/DMChat.tsx index 24412ae..b0f9b20 100644 --- a/web/src/components/DMChat.tsx +++ b/web/src/components/DMChat.tsx @@ -156,6 +156,7 @@ export function DMChat() { const fetchMessages = useConversationStore((s) => s.fetchMessages); const fetchOlderMessages = useConversationStore((s) => s.fetchOlderMessages); const isLoadingOlder = useConversationStore((s) => s.isLoadingOlder); + const hasMore = useConversationStore((s) => id ? s.hasMoreByConversation[id] !== false : true); const isLoading = useConversationStore((s) => s.isLoading); const sendMessage = useConversationStore((s) => s.sendMessage); const fetchConversations = useConversationStore((s) => s.fetchConversations); @@ -189,7 +190,7 @@ export function DMChat() { const handleScroll = useCallback(() => { const el = scrollContainerRef.current; - if (!el || !id || isLoadingOlder) return; + if (!el || !id || isLoadingOlder || !hasMore) return; if (el.scrollTop < 100) { const prevHeight = el.scrollHeight; fetchOlderMessages(id).then(() => { @@ -198,7 +199,7 @@ export function DMChat() { }); }); } - }, [id, isLoadingOlder, fetchOlderMessages]); + }, [id, isLoadingOlder, hasMore, fetchOlderMessages]); const messages = id ? messagesByConv[id] || [] : []; useEffect(() => {