aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-16 15:25:12 -0800
committerGitHub <noreply@github.com>2026-02-16 15:25:12 -0800
commitfeea1a814a212efd9b14b8f05e84104ace3c473a (patch)
tree32cb971915aaa560e6fa433a5b8aee3cfbd5f8cf
parent2321d5bcf17e416244e75f5727f0710f9cdd9a1a (diff)
parent4555ee294a5acdfbc4b1026d99df54d07c74ed97 (diff)
downloadneko-feea1a814a212efd9b14b8f05e84104ace3c473a.tar.gz
neko-feea1a814a212efd9b14b8f05e84104ace3c473a.tar.bz2
neko-feea1a814a212efd9b14b8f05e84104ace3c473a.zip
Merge pull request #6 from adammathes/claude/fix-mobile-scroll-read-yVmVk
Fix mobile scroll not marking items as read in v3 UI
-rw-r--r--frontend-vanilla/src/main.test.ts10
-rw-r--r--frontend-vanilla/src/main.ts11
2 files changed, 13 insertions, 8 deletions
diff --git a/frontend-vanilla/src/main.test.ts b/frontend-vanilla/src/main.test.ts
index 26e8019..436db14 100644
--- a/frontend-vanilla/src/main.test.ts
+++ b/frontend-vanilla/src/main.test.ts
@@ -259,7 +259,7 @@ describe('main application logic', () => {
expect(store.sidebarVisible).toBe(!initialVisible);
});
- it('should mark item as read when scrolled into view', () => {
+ it('should mark item as read when scrolled past', () => {
const mockItem = {
_id: 123,
title: 'Scroll Test Item',
@@ -277,13 +277,15 @@ describe('main application logic', () => {
vi.mocked(apiFetch).mockResolvedValue({ ok: true } as Response);
- // Simulate intersection
+ // Simulate item scrolled above viewport (no longer intersecting, bottom above root top)
const entry = {
target: itemEl,
- isIntersecting: true
+ isIntersecting: false,
+ boundingClientRect: { bottom: -10 } as DOMRectReadOnly,
+ rootBounds: { top: 0 } as DOMRectReadOnly,
} as IntersectionObserverEntry;
- // This relies on the LAST created observer's callback being captured.
+ // This relies on the LAST created observer's callback being captured.
expect(observerCallback).toBeDefined();
// @ts-ignore
observerCallback([entry], {} as IntersectionObserver);
diff --git a/frontend-vanilla/src/main.ts b/frontend-vanilla/src/main.ts
index 94f5727..c310144 100644
--- a/frontend-vanilla/src/main.ts
+++ b/frontend-vanilla/src/main.ts
@@ -262,6 +262,9 @@ export function renderItems() {
${store.hasMore ? '<div id="load-more-sentinel" class="loading-more">Loading more...</div>' : ''}
`;
+ // Use the actual scroll container as IntersectionObserver root
+ const scrollRoot = document.getElementById('main-content');
+
// Setup infinite scroll
const sentinel = document.getElementById('load-more-sentinel');
if (sentinel) {
@@ -269,14 +272,14 @@ export function renderItems() {
if (entries[0].isIntersecting && !store.loading && store.hasMore) {
loadMore();
}
- }, { threshold: 0.1 });
+ }, { root: scrollRoot, threshold: 0.1 });
observer.observe(sentinel);
}
- // Setup item observer for marking read
+ // Setup item observer for marking read when items scroll past (above viewport)
itemObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
- if (entry.isIntersecting) {
+ if (!entry.isIntersecting && entry.boundingClientRect.bottom < (entry.rootBounds?.top ?? 0)) {
const target = entry.target as HTMLElement;
const id = parseInt(target.getAttribute('data-id') || '0');
if (id) {
@@ -288,7 +291,7 @@ export function renderItems() {
}
}
});
- }, { threshold: 1.0 });
+ }, { root: scrollRoot, threshold: 0 });
contentArea.querySelectorAll('.feed-item').forEach(el => itemObserver!.observe(el));
}