From 72e131f9c273d15e8d3b5c8a9320ab7fb1d533d4 Mon Sep 17 00:00:00 2001
From: Adam Mathes <adam@adammathes.com>
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<IntersectionObserver | null>(null);
+  // Scroll listener to mark items as read
   const sentinelObserverRef = useRef<IntersectionObserver | null>(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

