Fix thumbnail loading and startup gallery refresh.
Refresh gallery metadata after thumbnail generation so new thumb URLs are available immediately, and lazy-load gallery thumbnails with IntersectionObserver to avoid fetching all images on initial page load. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -70,6 +70,17 @@ func New(imagesDir, staticDir string, mailCfg *mail.Config) (*Server, error) {
|
|||||||
} else {
|
} else {
|
||||||
log.Print("gallery thumbnails: ready")
|
log.Print("gallery thumbnails: ready")
|
||||||
}
|
}
|
||||||
|
// Thumbnail URLs are resolved during gallery.List. Re-list after derivative
|
||||||
|
// generation so newly-created thumbs appear immediately on first load.
|
||||||
|
if refreshed, err := gallery.List(imagesDir); err != nil {
|
||||||
|
log.Printf("gallery refresh after thumbnails: %v", err)
|
||||||
|
} else {
|
||||||
|
srv.Images = refreshed
|
||||||
|
if hero, hasHero := gallery.SelectHero(refreshed); hasHero {
|
||||||
|
srv.Hero = hero
|
||||||
|
srv.HasHero = true
|
||||||
|
}
|
||||||
|
}
|
||||||
return srv, nil
|
return srv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ templ GalleryGrid(images []gallery.Image) {
|
|||||||
>
|
>
|
||||||
if img.ThumbURL != "" {
|
if img.ThumbURL != "" {
|
||||||
<img
|
<img
|
||||||
src={ img.ThumbURL }
|
src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
|
||||||
|
data-src={ img.ThumbURL }
|
||||||
|
class="lazy-thumb"
|
||||||
alt={ fmt.Sprintf("%s — %s", gallery.AlbumLabel(img.Album), formatDate(img.Date)) }
|
alt={ fmt.Sprintf("%s — %s", gallery.AlbumLabel(img.Album), formatDate(img.Date)) }
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
|
|||||||
@@ -34,6 +34,47 @@ templ Layout(title string, preloadImage string, content templ.Component) {
|
|||||||
</footer>
|
</footer>
|
||||||
<div id="modal-root"></div>
|
<div id="modal-root"></div>
|
||||||
<script src="/static/htmx.min.js" defer></script>
|
<script src="/static/htmx.min.js" defer></script>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
function hydrateLazyThumbs(root) {
|
||||||
|
var lazyImages = root.querySelectorAll("img[data-src]");
|
||||||
|
if (!lazyImages.length) return;
|
||||||
|
|
||||||
|
if (!("IntersectionObserver" in window)) {
|
||||||
|
lazyImages.forEach(function (img) {
|
||||||
|
img.src = img.dataset.src;
|
||||||
|
img.removeAttribute("data-src");
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var observer = new IntersectionObserver(
|
||||||
|
function (entries) {
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (!entry.isIntersecting) return;
|
||||||
|
var img = entry.target;
|
||||||
|
img.src = img.dataset.src;
|
||||||
|
img.removeAttribute("data-src");
|
||||||
|
observer.unobserve(img);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ rootMargin: "300px 0px" },
|
||||||
|
);
|
||||||
|
|
||||||
|
lazyImages.forEach(function (img) {
|
||||||
|
observer.observe(img);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
hydrateLazyThumbs(document);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener("htmx:afterSwap", function (event) {
|
||||||
|
hydrateLazyThumbs(event.target);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user