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>
|
</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>
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -427,11 +458,12 @@
|
|||||||
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">
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user