Add pagination to feed with Load more button using HTMX

This commit is contained in:
2026-07-06 11:51:46 +02:00
parent 969fc673bc
commit bef4f7ba1d
5 changed files with 62 additions and 38 deletions
@@ -211,9 +211,11 @@ public class DatabaseService {
}
}
public List<Record.ReleaseGroup> getFeed(List<String> artistMbids, List<String> primaryTypes) {
public record FeedResult(List<Record.ReleaseGroup> entries, boolean hasMore) {}
public FeedResult getFeed(List<String> artistMbids, List<String> primaryTypes, int limit, int offset) {
var groups = new ArrayList<Record.ReleaseGroup>();
if (artistMbids.isEmpty()) return groups;
if (artistMbids.isEmpty()) return new FeedResult(groups, false);
var placeholders = String.join(",", artistMbids.stream().map(m -> "?").toList());
var sql = "SELECT * FROM release_group_cache WHERE artist_mbid IN (" + placeholders + ")";
@@ -222,12 +224,15 @@ public class DatabaseService {
sql += " AND primary_type IN (" + typePlaceholders + ")";
}
sql += " ORDER BY (CASE WHEN first_release_date IS NULL OR first_release_date = '' THEN 1 ELSE 0 END), first_release_date DESC";
sql += " LIMIT ? OFFSET ?";
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
int idx = 1;
for (var mbid : artistMbids) ps.setString(idx++, mbid);
for (var type : primaryTypes) ps.setString(idx++, type);
ps.setInt(idx++, limit + 1);
ps.setInt(idx, offset);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
groups.add(mapReleaseGroup(rs));
@@ -236,7 +241,9 @@ public class DatabaseService {
} catch (SQLException e) {
throw new RuntimeException(e);
}
return groups;
boolean hasMore = groups.size() > limit;
if (hasMore) groups.remove(groups.size() - 1);
return new FeedResult(groups, hasMore);
}
public void deleteReleaseGroupsForArtist(String mbid) {
@@ -1,6 +1,7 @@
package com.discdrop.service;
import com.discdrop.db.DatabaseService;
import com.discdrop.db.DatabaseService.FeedResult;
import com.discdrop.db.Record.ReleaseGroup;
import com.discdrop.db.Record.TrackedArtist;
import jakarta.enterprise.context.ApplicationScoped;
@@ -23,9 +24,15 @@ public class FeedService {
@Inject
DatabaseService db;
public static final int PAGE_SIZE = 20;
public List<ReleaseGroup> getFeedEntries() {
return getFeedEntries(1).entries();
}
public FeedResult getFeedEntries(int page) {
var artists = artistService.findAll();
if (artists.isEmpty()) return List.of();
if (artists.isEmpty()) return new FeedResult(List.of(), false);
var artistMbids = new ArrayList<String>();
for (var a : artists) {
@@ -42,7 +49,7 @@ public class FeedService {
monitoredTypes.addAll(getMonitoredTypes(a));
}
return db.getFeed(artistMbids, monitoredTypes.isEmpty() ? allTypes : monitoredTypes);
return db.getFeed(artistMbids, monitoredTypes.isEmpty() ? allTypes : monitoredTypes, PAGE_SIZE, (page - 1) * PAGE_SIZE);
}
private List<String> getMonitoredTypes(TrackedArtist a) {
@@ -1,5 +1,6 @@
package com.discdrop.web;
import com.discdrop.db.DatabaseService.FeedResult;
import com.discdrop.db.Record.MonitoringFlags;
import com.discdrop.db.Record.TrackedArtist;
import com.discdrop.service.ArtistService;
@@ -106,9 +107,12 @@ public class ArtistController {
@GET
@Path("/feed-table")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance feedTable() {
var entries = feedService.getFeedEntries();
return feedEntriesTemplate.data("entries", entries);
public TemplateInstance feedTable(@QueryParam("page") @DefaultValue("1") int page) {
var result = feedService.getFeedEntries(page);
return feedEntriesTemplate
.data("entries", result.entries())
.data("hasMore", result.hasMore())
.data("nextPage", page + 1);
}
private List<String> getMonitoredTypes(TrackedArtist a) {
@@ -4,9 +4,8 @@
<p class="text-sm text-base-content/40 mt-2">Add artists to your collection to see their releases here</p>
</div>
{#else}
<div style="display:flex;flex-direction:column;gap:2.5rem">
{#for entry in entries}
<a href="https://musicbrainz.org/release-group/{entry.mbid}" target="_blank" rel="noopener noreferrer"
{#for entry in entries}
<a href="https://musicbrainz.org/release-group/{entry.mbid}" target="_blank" rel="noopener noreferrer"
class="card card-side bg-base-200 border border-base-300 hover:bg-base-300 card-shadow overflow-hidden">
<figure class="w-64">
<img src="https://coverartarchive.org/release-group/{entry.mbid}/front-250"
@@ -34,7 +33,14 @@
</div>
</div>
</div>
</a>
{/for}
</a>
{/for}
{#if hasMore}
<div style="display:flex;justify-content:center;padding:1rem 0">
<button class="btn btn-primary btn-sm"
hx-get="/api/artists/feed-table?page={nextPage}"
hx-target="closest div"
hx-swap="outerHTML">Load more</button>
</div>
{/if}
{/if}
+1 -1
View File
@@ -17,7 +17,7 @@
</a>
</div>
</div>
<div id="feed-entries" hx-get="/api/artists/feed-table" hx-trigger="load">
<div id="feed-entries" hx-get="/api/artists/feed-table" hx-trigger="load" style="display:flex;flex-direction:column;gap:2.5rem">
<div style="display:flex;justify-content:center;padding:4rem 0">
<span class="loading loading-spinner loading-lg"></span>
</div>