fix some frontend issues

This commit is contained in:
2026-06-12 10:57:48 +02:00
parent ed1241490d
commit 0820ce6ef7
5 changed files with 383 additions and 23 deletions
+22 -9
View File
@@ -11,6 +11,7 @@ const showModal = ref(false)
const viewMode = ref('list') // 'list' | 'article' — toggled from the hamburger menu
const currentIndex = ref(0)
const layout = ref(localStorage.getItem('layout') || 'list') // 'list' | 'cards' — list-view display style, toggled from the hamburger menu
const navTitleVisible = ref(true) // whether AppNav's "RSS Reader (N)" title is currently in view
let observer; // Declare observer outside the setup function
let initialLoad = false
@@ -92,6 +93,11 @@ async function getReadable(feed, index) {
doc.head.prepend(base);
doc.querySelectorAll('img').forEach(resolveTemplatedImage);
doc.querySelectorAll('video, audio').forEach(el => el.remove());
// Some feeds (e.g. Stuttgarter Nachrichten) embed a social-sharing widget
// (WhatsApp/Email/Facebook/... links plus a "Link kopiert" tooltip) in the
// article body. It's not part of the article, so strip it before Readability
// pulls it into the parsed content.
doc.querySelectorAll('#article-social-bar').forEach(el => el.remove())
// Some feeds (e.g. Deutsche Welle) leave behind a heading + play-icon SVG
// for an embedded video/audio player whose actual <video>/<audio>/<iframe>
// we already stripped — without it, the heading is just a giant orphaned
@@ -160,9 +166,14 @@ function setupIntersectionObserver() {
observer.disconnect();
}
observer = new IntersectionObserver(handleIntersection, {
// The sticky topbar overlays the top of the viewport, so an article fully
// hidden behind it should already count as "scrolled past" — shrink the
// observer's root by that height so it stops intersecting at that point.
const topbarHeight = document.querySelector('.list-topbar')?.getBoundingClientRect().height ?? 0;
observer = new IntersectionObserver((entries) => handleIntersection(entries, topbarHeight), {
root: null, // Use the viewport as the root
rootMargin: '0px',
rootMargin: `-${topbarHeight}px 0px 0px 0px`,
// threshold: 0.5, // Fire the callback when at least 50% of the element is visible
});
@@ -174,14 +185,15 @@ function setupIntersectionObserver() {
}
}
async function handleIntersection(entries) {
// An article that has scrolled above the viewport (not intersecting,
// bounding box above the top edge) has been read. Resolve all affected
// feeds up front, before any removal — splicing `feeds` while iterating
// would shift the array indices that later entries' `target.id` refer to,
// causing the wrong item to be marked read and removed.
async function handleIntersection(entries, topbarHeight = 0) {
// An article that has scrolled past the (possibly sticky-bar-shrunk) top
// edge of the viewport (not intersecting, bounding box above that edge)
// has been read. Resolve all affected feeds up front, before any removal —
// splicing `feeds` while iterating would shift the array indices that later
// entries' `target.id` refer to, causing the wrong item to be marked read
// and removed.
const readFeeds = entries
.filter(entry => initialLoad === true && !entry.isIntersecting && entry.boundingClientRect.y < 0)
.filter(entry => initialLoad === true && !entry.isIntersecting && entry.boundingClientRect.y < topbarHeight)
.map(entry => feeds.value[entry.target.id])
.filter(Boolean)
@@ -280,6 +292,7 @@ export function useFeeds() {
leaveArticleView,
layout,
toggleLayout,
navTitleVisible,
nextArticle,
prevArticle,
fetchData,