From 72e131f9c273d15e8d3b5c8a9320ab7fb1d533d4 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Mon, 16 Feb 2026 16:35:38 -0800 Subject: Fix scroll-to-read functionality across all UIs (V1, V2, V3) --- frontend/src/components/FeedItems.tsx | 75 +++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 26 deletions(-) (limited to 'frontend/src/components/FeedItems.tsx') diff --git a/frontend/src/components/FeedItems.tsx b/frontend/src/components/FeedItems.tsx index ea5d8fd..e2df011 100644 --- a/frontend/src/components/FeedItems.tsx +++ b/frontend/src/components/FeedItems.tsx @@ -198,39 +198,62 @@ export default function FeedItems() { }, [markAsRead, scrollToItem, toggleStar, fetchItems]); - // Stable Observer - const observerRef = useRef(null); + // Scroll listener to mark items as read const sentinelObserverRef = useRef(null); - useEffect(() => { - if (observerRef.current) observerRef.current.disconnect(); - - observerRef.current = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (!entry.isIntersecting && entry.boundingClientRect.top < 0) { - const index = Number(entry.target.getAttribute('data-index')); - const currentItems = itemsRef.current; - if (!isNaN(index) && index >= 0 && index < currentItems.length) { - const item = currentItems[index]; - if (!item.read) { - markAsRead(item); - } - } - } - }); - }, - { root: null, threshold: 0 } - ); + const checkReadStatus = useCallback(() => { + const container = document.querySelector('.dashboard-main'); + if (!container) return; + const containerRect = container.getBoundingClientRect(); const currentItems = itemsRef.current; - currentItems.forEach((_, index) => { + + currentItems.forEach((item, index) => { + if (item.read) return; + const el = document.getElementById(`item-${index}`); - if (el) observerRef.current?.observe(el); + if (!el) return; + + const rect = el.getBoundingClientRect(); + + // Mark as read if the top of the item is above the top of the container + if (rect.top < containerRect.top) { + markAsRead(item); + } }); + }, [markAsRead]); + + // Setup scroll listener + useEffect(() => { + const container = document.querySelector('.dashboard-main'); + if (!container) return; + + let timeoutId: number | null = null; + const onScroll = () => { + if (timeoutId === null) { + timeoutId = window.setTimeout(() => { + checkReadStatus(); + timeoutId = null; + }, 250); + } + }; + + container.addEventListener('scroll', onScroll); + + // Initial check + checkReadStatus(); + + return () => { + if (timeoutId) clearTimeout(timeoutId); + container.removeEventListener('scroll', onScroll); + }; + }, [checkReadStatus]); + + // Re-check when items change (e.g. initial load or load more) + useEffect(() => { + checkReadStatus(); + }, [items, checkReadStatus]); - return () => observerRef.current?.disconnect(); - }, [items.length, markAsRead]); // Only re-setup if item count changes useEffect(() => { -- cgit v1.2.3