Files
decePubClient/wwwroot/js/feed.js
T
thepraandClaude Opus 5.5 697b2c8897
Build / Build (push) Successful in 33s
Animations, GIFs and the globe now move for every reader
decePub no longer looks at the system's "reduce motion" setting, so everything moves the same way whatever it says:
- a GIF video in a post plays by itself, muted and looping, instead of waiting for a tap; it now also starts on a page
  just opened, before the first tap, where the browser used to hold it still;
- animated custom emoji move in the emoji picker instead of showing a still picture;
- the globe turns by itself while no located post is in view, flies to the posts, and its pulses, arcs and Sun move;
- going back to the top of a feed scrolls smoothly;
- tooltips, dropdowns, menus, folding sections, toasts, loading spinners, placeholders, progress bars, radio buttons
  and ratings keep their animations: the build now takes daisyUI's reduce-motion rules out of the stylesheet
  (tools/css/motion.py, run after every Tailwind build, in watch mode too).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4YtEKqxKjKyHkFzuRVMNA
2026-11-09 20:48:47 +01:00

62 lines
1.7 KiB
JavaScript

// The feed page's scrolling (Pages/Index.razor): tells .NET when the end of the list comes near, so the next page loads
// before the reader gets there, and whether the page is visible, so polling for new posts stops in a hidden tab.
let observer = null
let sentinel = null
let reference = null
export function observeEnd(dotNetReference, sentinelId, rootId) {
dispose()
reference = dotNetReference
sentinel = document.getElementById(sentinelId)
const root = document.getElementById(rootId)
if (!sentinel || !root)
return
observer = new IntersectionObserver(entries => {
if (entries.some(entry => entry.isIntersecting))
nearEnd()
}, { root, rootMargin: "0px 0px 800px 0px" })
observer.observe(sentinel)
}
// After a page is added: if the end is still in sight (a short page), ask for the next one too.
export function checkEnd() {
if (!sentinel || !observer)
return
const root = observer.root
const end = sentinel.getBoundingClientRect().top
const bottom = root.getBoundingClientRect().bottom
if (end < bottom + 800)
nearEnd()
}
export function isVisible() {
return document.visibilityState === "visible"
}
// The scroll a feed was left at, and the way back to it (Services/FeedMemory.cs).
export function scrollOffset(rootId) {
return document.getElementById(rootId)?.scrollTop ?? 0
}
export function scrollToOffset(rootId, top) {
const root = document.getElementById(rootId)
if (root)
root.scrollTop = top
}
export function scrollToTop(rootId) {
document.getElementById(rootId)?.scrollTo({ top: 0, behavior: "smooth" })
}
export function dispose() {
observer?.disconnect()
observer = null
sentinel = null
reference = null
}
function nearEnd() {
reference?.invokeMethodAsync("NearEnd").catch(e => console.error(e))
}