aboutsummaryrefslogtreecommitdiffstats
path: root/frontend/src/components/FeedItems.tsx
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-16 16:35:38 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-16 16:35:38 -0800
commit72e131f9c273d15e8d3b5c8a9320ab7fb1d533d4 (patch)
tree8d02878300210d412f88d2890e42f3de8f1e8b80 /frontend/src/components/FeedItems.tsx
parentf04d8fbd9900d5326cdd65a725e57f4b08bbd655 (diff)
downloadneko-72e131f9c273d15e8d3b5c8a9320ab7fb1d533d4.tar.gz
neko-72e131f9c273d15e8d3b5c8a9320ab7fb1d533d4.tar.bz2
neko-72e131f9c273d15e8d3b5c8a9320ab7fb1d533d4.zip
Fix scroll-to-read functionality across all UIs (V1, V2, V3)
Diffstat (limited to 'frontend/src/components/FeedItems.tsx')
-rw-r--r--frontend/src/components/FeedItems.tsx75
1 files changed, 49 insertions, 26 deletions
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(() => {