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:
@@ -452,10 +452,9 @@
|
||||
</button>
|
||||
{/each}
|
||||
<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(); }}>
|
||||
#{tag.name}
|
||||
<span class="pill-count">{tag.item_count}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
let autoScrollSpeed = $state(1.5);
|
||||
let articleListEl: HTMLDivElement;
|
||||
let scrollRAF: number | null = null;
|
||||
let lastScrollTs = 0;
|
||||
let loading = $state(true);
|
||||
let loadingMore = $state(false);
|
||||
let hasMore = $state(true);
|
||||
@@ -315,22 +316,31 @@
|
||||
if (scrollRAF) cancelAnimationFrame(scrollRAF);
|
||||
if (!usesPageScroll() && !articleListEl) return;
|
||||
autoScrollActive = true;
|
||||
function step() {
|
||||
lastScrollTs = 0;
|
||||
function step(timestamp: number) {
|
||||
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()) {
|
||||
const nextY = window.scrollY + autoScrollSpeed;
|
||||
const maxY = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
|
||||
window.scrollTo({ top: Math.min(nextY, maxY), behavior: 'auto' });
|
||||
if (nextY >= maxY) {
|
||||
const scroller = document.scrollingElement;
|
||||
if (!scroller) return;
|
||||
const nextY = scroller.scrollTop + delta;
|
||||
const maxY = Math.max(0, scroller.scrollHeight - window.innerHeight);
|
||||
scroller.scrollTop = Math.min(nextY, maxY);
|
||||
checkScrolledCards();
|
||||
if (nextY >= maxY - 1) {
|
||||
stopAutoScroll();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!articleListEl) return;
|
||||
articleListEl.scrollTop += autoScrollSpeed;
|
||||
articleListEl.scrollTop += delta;
|
||||
const maxScroll = articleListEl.scrollHeight - articleListEl.clientHeight;
|
||||
if (articleListEl.scrollTop >= maxScroll) {
|
||||
checkScrolledCards();
|
||||
if (articleListEl.scrollTop >= maxScroll - 1) {
|
||||
stopAutoScroll();
|
||||
return;
|
||||
}
|
||||
@@ -342,6 +352,7 @@
|
||||
}
|
||||
function stopAutoScroll() {
|
||||
autoScrollActive = false;
|
||||
lastScrollTs = 0;
|
||||
if (scrollRAF) { cancelAnimationFrame(scrollRAF); scrollRAF = null; }
|
||||
}
|
||||
function toggleAutoScroll() {
|
||||
@@ -366,6 +377,7 @@
|
||||
let scrollCheckTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function handleListScroll() {
|
||||
if (usesPageScroll()) return;
|
||||
// Throttle: only check every 400ms
|
||||
if (scrollCheckTimer) return;
|
||||
scrollCheckTimer = setTimeout(() => {
|
||||
@@ -374,21 +386,40 @@
|
||||
}, 400);
|
||||
}
|
||||
|
||||
function handleViewportScroll() {
|
||||
if (!usesPageScroll()) return;
|
||||
if (scrollCheckTimer) return;
|
||||
scrollCheckTimer = setTimeout(() => {
|
||||
scrollCheckTimer = null;
|
||||
checkScrolledCards();
|
||||
}, 240);
|
||||
}
|
||||
|
||||
function checkScrolledCards() {
|
||||
if (!articleListEl) return;
|
||||
|
||||
// Infinite scroll — load more when near bottom
|
||||
const { scrollTop, scrollHeight, clientHeight } = articleListEl;
|
||||
if (hasMore && !loadingMore && scrollHeight - scrollTop - clientHeight < 300) {
|
||||
loadEntries(true);
|
||||
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;
|
||||
if (hasMore && !loadingMore && scrollHeight - scrollTop - clientHeight < 300) {
|
||||
loadEntries(true);
|
||||
}
|
||||
}
|
||||
|
||||
const listTop = articleListEl.getBoundingClientRect().top;
|
||||
const cards = articleListEl.querySelectorAll('[data-entry-id]');
|
||||
let newlyRead = 0;
|
||||
|
||||
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'));
|
||||
if (!id) return;
|
||||
const article = articles.find(a => a.id === id);
|
||||
@@ -422,16 +453,17 @@
|
||||
}
|
||||
|
||||
// ── Init ──
|
||||
onMount(() => {
|
||||
onMount(() => {
|
||||
loadSidebar();
|
||||
loadEntries();
|
||||
return () => {
|
||||
if (flushTimer) clearTimeout(flushTimer);
|
||||
if (scrollCheckTimer) clearTimeout(scrollCheckTimer);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={handleKeydown} />
|
||||
<svelte:window onkeydown={handleKeydown} onscroll={handleViewportScroll} />
|
||||
|
||||
<div class="reader-layout">
|
||||
|
||||
|
||||
Reference in New Issue
Block a user