fix: mobile tags pills show all active tags regardless of item count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-01 22:42:56 -05:00
parent 9f51f3bcd3
commit 2ab27d048a
2 changed files with 49 additions and 18 deletions

View File

@@ -452,10 +452,9 @@
</button> </button>
{/each} {/each}
<span class="pill-separator"></span> <span class="pill-separator"></span>
{#each sidebarTags.filter(t => t.is_active && t.item_count > 0) as tag} {#each sidebarTags.filter(t => t.is_active) as tag}
<button class="pill pill-tag" class:active={activeTag === tag.name} onclick={() => { activeTag = tag.name; activeTagId = tag.id; activeFolder = null; activeFolderId = null; loadItems(); }}> <button class="pill pill-tag" class:active={activeTag === tag.name} onclick={() => { activeTag = tag.name; activeTagId = tag.id; activeFolder = null; activeFolderId = null; loadItems(); }}>
#{tag.name} #{tag.name}
<span class="pill-count">{tag.item_count}</span>
</button> </button>
{/each} {/each}
</div> </div>

View File

@@ -27,6 +27,7 @@
let autoScrollSpeed = $state(1.5); let autoScrollSpeed = $state(1.5);
let articleListEl: HTMLDivElement; let articleListEl: HTMLDivElement;
let scrollRAF: number | null = null; let scrollRAF: number | null = null;
let lastScrollTs = 0;
let loading = $state(true); let loading = $state(true);
let loadingMore = $state(false); let loadingMore = $state(false);
let hasMore = $state(true); let hasMore = $state(true);
@@ -315,22 +316,31 @@
if (scrollRAF) cancelAnimationFrame(scrollRAF); if (scrollRAF) cancelAnimationFrame(scrollRAF);
if (!usesPageScroll() && !articleListEl) return; if (!usesPageScroll() && !articleListEl) return;
autoScrollActive = true; autoScrollActive = true;
function step() { lastScrollTs = 0;
function step(timestamp: number) {
if (!autoScrollActive) return; if (!autoScrollActive) return;
const dt = lastScrollTs ? Math.min(34, timestamp - lastScrollTs) : 16;
lastScrollTs = timestamp;
const pxPerSecond = 28 * autoScrollSpeed;
const delta = pxPerSecond * (dt / 1000);
if (usesPageScroll()) { if (usesPageScroll()) {
const nextY = window.scrollY + autoScrollSpeed; const scroller = document.scrollingElement;
const maxY = Math.max(0, document.documentElement.scrollHeight - window.innerHeight); if (!scroller) return;
window.scrollTo({ top: Math.min(nextY, maxY), behavior: 'auto' }); const nextY = scroller.scrollTop + delta;
if (nextY >= maxY) { const maxY = Math.max(0, scroller.scrollHeight - window.innerHeight);
scroller.scrollTop = Math.min(nextY, maxY);
checkScrolledCards();
if (nextY >= maxY - 1) {
stopAutoScroll(); stopAutoScroll();
return; return;
} }
} else { } else {
if (!articleListEl) return; if (!articleListEl) return;
articleListEl.scrollTop += autoScrollSpeed; articleListEl.scrollTop += delta;
const maxScroll = articleListEl.scrollHeight - articleListEl.clientHeight; const maxScroll = articleListEl.scrollHeight - articleListEl.clientHeight;
if (articleListEl.scrollTop >= maxScroll) { checkScrolledCards();
if (articleListEl.scrollTop >= maxScroll - 1) {
stopAutoScroll(); stopAutoScroll();
return; return;
} }
@@ -342,6 +352,7 @@
} }
function stopAutoScroll() { function stopAutoScroll() {
autoScrollActive = false; autoScrollActive = false;
lastScrollTs = 0;
if (scrollRAF) { cancelAnimationFrame(scrollRAF); scrollRAF = null; } if (scrollRAF) { cancelAnimationFrame(scrollRAF); scrollRAF = null; }
} }
function toggleAutoScroll() { function toggleAutoScroll() {
@@ -366,6 +377,7 @@
let scrollCheckTimer: ReturnType<typeof setTimeout> | null = null; let scrollCheckTimer: ReturnType<typeof setTimeout> | null = null;
function handleListScroll() { function handleListScroll() {
if (usesPageScroll()) return;
// Throttle: only check every 400ms // Throttle: only check every 400ms
if (scrollCheckTimer) return; if (scrollCheckTimer) return;
scrollCheckTimer = setTimeout(() => { scrollCheckTimer = setTimeout(() => {
@@ -374,21 +386,40 @@
}, 400); }, 400);
} }
function handleViewportScroll() {
if (!usesPageScroll()) return;
if (scrollCheckTimer) return;
scrollCheckTimer = setTimeout(() => {
scrollCheckTimer = null;
checkScrolledCards();
}, 240);
}
function checkScrolledCards() { function checkScrolledCards() {
if (!articleListEl) return; if (!articleListEl) return;
// Infinite scroll — load more when near bottom const cards = articleListEl.querySelectorAll('[data-entry-id]');
if (usesPageScroll()) {
const pageBottom = window.scrollY + window.innerHeight;
const loadThreshold = document.documentElement.scrollHeight - 500;
if (hasMore && !loadingMore && pageBottom >= loadThreshold) {
loadEntries(true);
}
} else {
const { scrollTop, scrollHeight, clientHeight } = articleListEl; const { scrollTop, scrollHeight, clientHeight } = articleListEl;
if (hasMore && !loadingMore && scrollHeight - scrollTop - clientHeight < 300) { if (hasMore && !loadingMore && scrollHeight - scrollTop - clientHeight < 300) {
loadEntries(true); loadEntries(true);
} }
}
const listTop = articleListEl.getBoundingClientRect().top;
const cards = articleListEl.querySelectorAll('[data-entry-id]');
let newlyRead = 0; let newlyRead = 0;
cards.forEach(card => { cards.forEach(card => {
if (card.getBoundingClientRect().bottom < listTop + 20) { const rect = card.getBoundingClientRect();
const passedThreshold = usesPageScroll()
? rect.bottom < 84
: rect.bottom < articleListEl.getBoundingClientRect().top + 20;
if (passedThreshold) {
const id = Number(card.getAttribute('data-entry-id')); const id = Number(card.getAttribute('data-entry-id'));
if (!id) return; if (!id) return;
const article = articles.find(a => a.id === id); const article = articles.find(a => a.id === id);
@@ -422,16 +453,17 @@
} }
// ── Init ── // ── Init ──
onMount(() => { onMount(() => {
loadSidebar(); loadSidebar();
loadEntries(); loadEntries();
return () => { return () => {
if (flushTimer) clearTimeout(flushTimer); if (flushTimer) clearTimeout(flushTimer);
if (scrollCheckTimer) clearTimeout(scrollCheckTimer);
}; };
}); });
</script> </script>
<svelte:window onkeydown={handleKeydown} /> <svelte:window onkeydown={handleKeydown} onscroll={handleViewportScroll} />
<div class="reader-layout"> <div class="reader-layout">