Fix: static path (META-INF/resources), settings @Transactional, search dropdown position, async follow + feed auto-refresh

This commit is contained in:
2026-07-15 10:46:22 +02:00
parent 4416e87740
commit 684f2d320a
14 changed files with 64 additions and 36 deletions
@@ -29,8 +29,10 @@ public class AppSetting extends PanacheEntity {
if (s == null) {
s = new AppSetting();
s.key = key;
s.value = value;
s.persist();
} else {
s.value = value;
}
s.value = value;
}
}
@@ -19,6 +19,7 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Path("/artists")
public class ArtistResource {
@@ -36,6 +37,10 @@ public class ArtistResource {
@Location("fragments/feed-list.html")
Template fragments_feed_list;
@Inject
@Location("fragments/feed-row.html")
Template fragments_feed_row;
@Inject
@Location("fragments/artist-row.html")
Template fragments_artist_row;
@@ -61,16 +66,16 @@ public class ArtistResource {
dto.area = new ArtistSearchResult.ArtistDto.Area();
dto.area.name = area;
}
FollowedArtist artist = artistService.follow(dto);
syncService.syncArtist(artist.mbid);
return feedFragment(0);
artistService.follow(dto);
CompletableFuture.runAsync(() -> syncService.syncArtist(mbid));
return feedFragment(0, true);
}
@DELETE
@Path("/{mbid}")
public TemplateInstance unfollow(@PathParam("mbid") String mbid) {
artistService.unfollow(mbid);
return feedFragment(0);
return feedFragment(0, false);
}
@GET
@@ -96,12 +101,12 @@ public class ArtistResource {
FollowedArtist artist = artistService.findArtist(mbid);
if (artist != null) {
artistService.setTypeEnabled(artist, primaryType, enabled);
syncService.resyncArtist(mbid);
CompletableFuture.runAsync(() -> syncService.resyncArtist(mbid));
}
return feedFragment(0);
return feedFragment(0, true);
}
private TemplateInstance feedFragment(int offset) {
private TemplateInstance feedFragment(int offset, boolean autoRefresh) {
if (offset < 0) {
offset = 0;
}
@@ -111,6 +116,7 @@ public class ArtistResource {
return fragments_feed_list.data("rows", rows)
.data("nextOffset", nextOffset)
.data("hasMore", hasMore)
.data("pageSize", feedService.pageSize());
.data("pageSize", feedService.pageSize())
.data("autoRefresh", autoRefresh);
}
}
@@ -40,7 +40,8 @@ public class PageResource {
return fragments_feed_list.data("rows", rows)
.data("nextOffset", nextOffset)
.data("hasMore", hasMore)
.data("pageSize", feedService.pageSize());
.data("pageSize", feedService.pageSize())
.data("autoRefresh", false);
}
@GET
@@ -52,6 +53,7 @@ public class PageResource {
.data("nextOffset", rows.size())
.data("hasMore", hasMore)
.data("pageSize", feedService.pageSize())
.data("autoRefresh", false)
.data("followedCount", FollowedArtist.count())
.data("feedCount", ReleaseGroupEntity.count());
}
@@ -2,6 +2,7 @@ package io.discdrop.service;
import io.discdrop.persistence.AppSetting;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.transaction.Transactional;
import java.util.Arrays;
import java.util.LinkedHashSet;
@@ -22,6 +23,7 @@ public class SettingsService {
return parseTypes(raw);
}
@Transactional
public void setDefaultPrimaryTypes(Set<String> types) {
AppSetting.set(KEY_DEFAULT_PRIMARY_TYPES, String.join(",", types));
}
@@ -35,6 +37,7 @@ public class SettingsService {
}
}
@Transactional
public void setSyncScheduleHours(int hours) {
AppSetting.set(KEY_SYNC_SCHEDULE_HOURS, String.valueOf(hours));
}
@@ -0,0 +1,12 @@
/* DiscDrop custom styles */
html { scroll-behavior: smooth; }
/* Hide empty search dropdown */
#search-dropdown:empty { display: none; }
/* Remove native search input artifacts */
input[type="text"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
appearance: none;
}

Before

Width:  |  Height:  |  Size: 469 B

After

Width:  |  Height:  |  Size: 469 B

Before

Width:  |  Height:  |  Size: 676 B

After

Width:  |  Height:  |  Size: 676 B

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 418 B

-3
View File
@@ -1,3 +0,0 @@
/* DiscDrop custom styles */
html { scroll-behavior: smooth; }
#search-dropdown:empty { display: none; }
+4 -4
View File
@@ -4,18 +4,18 @@
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>DiscDrop Artists</title>
<link rel="icon" type="image/svg+xml" href="/static/img/favicon.svg"/>
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg"/>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.min.css" rel="stylesheet" type="text/css"/>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/[email protected]"></script>
<link rel="stylesheet" href="/static/css/app.css"/>
<script src="/static/js/app.js" defer></script>
<link rel="stylesheet" href="/css/app.css"/>
<script src="/js/app.js" defer></script>
</head>
<body>
<header class="sticky top-0 z-30 bg-base-100/90 backdrop-blur border-b border-base-300">
<div class="navbar flex flex-wrap justify-center gap-4 px-4 py-3 max-w-5xl mx-auto">
<div class="flex items-center gap-2">
<img src="/static/img/logo.svg" alt="DiscDrop logo" class="h-9 w-9"/>
<img src="/img/logo.svg" alt="DiscDrop logo" class="h-9 w-9"/>
<span class="text-xl font-bold">DiscDrop</span>
</div>
<div class="flex items-center gap-2">
@@ -16,3 +16,10 @@
<p class="text-sm">Search for an artist above and click <strong>Follow</strong> to start tracking their release groups.</p>
</div>
{/if}
{#if autoRefresh?? && autoRefresh}
<div hx-get="/feed?offset=0"
hx-target="#feed-list"
hx-swap="innerHTML"
hx-trigger="load delay:3s"
class="hidden"></div>
{/if}
@@ -3,7 +3,7 @@
alt="cover"
loading="lazy"
class="w-16 h-16 rounded object-cover bg-base-200"
onerror="this.onerror=null;this.src='/static/img/no-cover.svg'"/>
onerror="this.onerror=null;this.src='/img/no-cover.svg'"/>
<div class="flex-1 min-w-0">
<div class="flex flex-wrap items-center gap-2">
+17 -18
View File
@@ -4,38 +4,37 @@
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>DiscDrop</title>
<link rel="icon" type="image/svg+xml" href="/static/img/favicon.svg"/>
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg"/>
<link rel="alternate" type="application/rss+xml" title="DiscDrop New Releases" href="/rss"/>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.min.css" rel="stylesheet" type="text/css"/>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/[email protected]"></script>
<link rel="stylesheet" href="/static/css/app.css"/>
<script src="/static/js/app.js" defer></script>
<link rel="stylesheet" href="/css/app.css"/>
<script src="/js/app.js" defer></script>
</head>
<body>
<header class="sticky top-0 z-30 bg-base-100/90 backdrop-blur border-b border-base-300">
<div class="navbar flex flex-wrap justify-center gap-4 px-4 py-3 max-w-5xl mx-auto">
<div class="flex items-center gap-2">
<img src="/static/img/logo.svg" alt="DiscDrop logo" class="h-9 w-9"/>
<img src="/img/logo.svg" alt="DiscDrop logo" class="h-9 w-9"/>
<span class="text-xl font-bold">DiscDrop</span>
</div>
<div id="search-wrap" class="relative flex-1 min-w-[220px] max-w-xl">
<form id="search-form" class="join w-full" autocomplete="off">
<input
id="search-input"
type="search"
name="q"
placeholder="Search MusicBrainz artists…"
class="input input-bordered join-item w-full"
hx-get="/search/artists"
hx-trigger="input changed delay:300ms"
hx-target="#search-dropdown"
hx-swap="innerHTML"
hx-include="this"/>
</form>
<input
id="search-input"
type="text"
name="q"
placeholder="Search MusicBrainz artists…"
class="input input-bordered w-full"
autocomplete="off"
hx-get="/search/artists"
hx-trigger="input changed delay:300ms"
hx-target="#search-dropdown"
hx-swap="innerHTML"
hx-include="this"/>
<div id="search-dropdown"
class="absolute left-0 right-0 z-40 mt-1 bg-base-100 rounded-box shadow-lg border border-base-300 max-h-96 overflow-y-auto"></div>
class="absolute left-0 right-0 top-full z-40 mt-1 bg-base-100 rounded-box shadow-lg border border-base-300 max-h-96 overflow-y-auto"></div>
</div>
<div class="flex items-center gap-2">