Fix: static path (META-INF/resources), settings @Transactional, search dropdown position, async follow + feed auto-refresh
This commit is contained in:
@@ -29,8 +29,10 @@ public class AppSetting extends PanacheEntity {
|
|||||||
if (s == null) {
|
if (s == null) {
|
||||||
s = new AppSetting();
|
s = new AppSetting();
|
||||||
s.key = key;
|
s.key = key;
|
||||||
|
s.value = value;
|
||||||
s.persist();
|
s.persist();
|
||||||
|
} else {
|
||||||
|
s.value = value;
|
||||||
}
|
}
|
||||||
s.value = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import jakarta.ws.rs.Path;
|
|||||||
import jakarta.ws.rs.PathParam;
|
import jakarta.ws.rs.PathParam;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@Path("/artists")
|
@Path("/artists")
|
||||||
public class ArtistResource {
|
public class ArtistResource {
|
||||||
@@ -36,6 +37,10 @@ public class ArtistResource {
|
|||||||
@Location("fragments/feed-list.html")
|
@Location("fragments/feed-list.html")
|
||||||
Template fragments_feed_list;
|
Template fragments_feed_list;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@Location("fragments/feed-row.html")
|
||||||
|
Template fragments_feed_row;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Location("fragments/artist-row.html")
|
@Location("fragments/artist-row.html")
|
||||||
Template fragments_artist_row;
|
Template fragments_artist_row;
|
||||||
@@ -61,16 +66,16 @@ public class ArtistResource {
|
|||||||
dto.area = new ArtistSearchResult.ArtistDto.Area();
|
dto.area = new ArtistSearchResult.ArtistDto.Area();
|
||||||
dto.area.name = area;
|
dto.area.name = area;
|
||||||
}
|
}
|
||||||
FollowedArtist artist = artistService.follow(dto);
|
artistService.follow(dto);
|
||||||
syncService.syncArtist(artist.mbid);
|
CompletableFuture.runAsync(() -> syncService.syncArtist(mbid));
|
||||||
return feedFragment(0);
|
return feedFragment(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("/{mbid}")
|
@Path("/{mbid}")
|
||||||
public TemplateInstance unfollow(@PathParam("mbid") String mbid) {
|
public TemplateInstance unfollow(@PathParam("mbid") String mbid) {
|
||||||
artistService.unfollow(mbid);
|
artistService.unfollow(mbid);
|
||||||
return feedFragment(0);
|
return feedFragment(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@@ -96,12 +101,12 @@ public class ArtistResource {
|
|||||||
FollowedArtist artist = artistService.findArtist(mbid);
|
FollowedArtist artist = artistService.findArtist(mbid);
|
||||||
if (artist != null) {
|
if (artist != null) {
|
||||||
artistService.setTypeEnabled(artist, primaryType, enabled);
|
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) {
|
if (offset < 0) {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
}
|
}
|
||||||
@@ -111,6 +116,7 @@ public class ArtistResource {
|
|||||||
return fragments_feed_list.data("rows", rows)
|
return fragments_feed_list.data("rows", rows)
|
||||||
.data("nextOffset", nextOffset)
|
.data("nextOffset", nextOffset)
|
||||||
.data("hasMore", hasMore)
|
.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)
|
return fragments_feed_list.data("rows", rows)
|
||||||
.data("nextOffset", nextOffset)
|
.data("nextOffset", nextOffset)
|
||||||
.data("hasMore", hasMore)
|
.data("hasMore", hasMore)
|
||||||
.data("pageSize", feedService.pageSize());
|
.data("pageSize", feedService.pageSize())
|
||||||
|
.data("autoRefresh", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@@ -52,6 +53,7 @@ public class PageResource {
|
|||||||
.data("nextOffset", rows.size())
|
.data("nextOffset", rows.size())
|
||||||
.data("hasMore", hasMore)
|
.data("hasMore", hasMore)
|
||||||
.data("pageSize", feedService.pageSize())
|
.data("pageSize", feedService.pageSize())
|
||||||
|
.data("autoRefresh", false)
|
||||||
.data("followedCount", FollowedArtist.count())
|
.data("followedCount", FollowedArtist.count())
|
||||||
.data("feedCount", ReleaseGroupEntity.count());
|
.data("feedCount", ReleaseGroupEntity.count());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package io.discdrop.service;
|
|||||||
|
|
||||||
import io.discdrop.persistence.AppSetting;
|
import io.discdrop.persistence.AppSetting;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@@ -22,6 +23,7 @@ public class SettingsService {
|
|||||||
return parseTypes(raw);
|
return parseTypes(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
public void setDefaultPrimaryTypes(Set<String> types) {
|
public void setDefaultPrimaryTypes(Set<String> types) {
|
||||||
AppSetting.set(KEY_DEFAULT_PRIMARY_TYPES, String.join(",", types));
|
AppSetting.set(KEY_DEFAULT_PRIMARY_TYPES, String.join(",", types));
|
||||||
}
|
}
|
||||||
@@ -35,6 +37,7 @@ public class SettingsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
public void setSyncScheduleHours(int hours) {
|
public void setSyncScheduleHours(int hours) {
|
||||||
AppSetting.set(KEY_SYNC_SCHEDULE_HOURS, String.valueOf(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 |
@@ -1,3 +0,0 @@
|
|||||||
/* DiscDrop custom styles */
|
|
||||||
html { scroll-behavior: smooth; }
|
|
||||||
#search-dropdown:empty { display: none; }
|
|
||||||
@@ -4,18 +4,18 @@
|
|||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>DiscDrop – Artists</title>
|
<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"/>
|
<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://cdn.tailwindcss.com"></script>
|
||||||
<script src="https://unpkg.com/[email protected]"></script>
|
<script src="https://unpkg.com/[email protected]"></script>
|
||||||
<link rel="stylesheet" href="/static/css/app.css"/>
|
<link rel="stylesheet" href="/css/app.css"/>
|
||||||
<script src="/static/js/app.js" defer></script>
|
<script src="/js/app.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="sticky top-0 z-30 bg-base-100/90 backdrop-blur border-b border-base-300">
|
<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="navbar flex flex-wrap justify-center gap-4 px-4 py-3 max-w-5xl mx-auto">
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<span class="text-xl font-bold">DiscDrop</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<p class="text-sm">Search for an artist above and click <strong>Follow</strong> to start tracking their release groups.</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/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"
|
alt="cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="w-16 h-16 rounded object-cover bg-base-200"
|
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-1 min-w-0">
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
|||||||
@@ -4,38 +4,37 @@
|
|||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>DiscDrop</title>
|
<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 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"/>
|
<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://cdn.tailwindcss.com"></script>
|
||||||
<script src="https://unpkg.com/[email protected]"></script>
|
<script src="https://unpkg.com/[email protected]"></script>
|
||||||
<link rel="stylesheet" href="/static/css/app.css"/>
|
<link rel="stylesheet" href="/css/app.css"/>
|
||||||
<script src="/static/js/app.js" defer></script>
|
<script src="/js/app.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="sticky top-0 z-30 bg-base-100/90 backdrop-blur border-b border-base-300">
|
<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="navbar flex flex-wrap justify-center gap-4 px-4 py-3 max-w-5xl mx-auto">
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<span class="text-xl font-bold">DiscDrop</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="search-wrap" class="relative flex-1 min-w-[220px] max-w-xl">
|
<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
|
||||||
<input
|
id="search-input"
|
||||||
id="search-input"
|
type="text"
|
||||||
type="search"
|
name="q"
|
||||||
name="q"
|
placeholder="Search MusicBrainz artists…"
|
||||||
placeholder="Search MusicBrainz artists…"
|
class="input input-bordered w-full"
|
||||||
class="input input-bordered join-item w-full"
|
autocomplete="off"
|
||||||
hx-get="/search/artists"
|
hx-get="/search/artists"
|
||||||
hx-trigger="input changed delay:300ms"
|
hx-trigger="input changed delay:300ms"
|
||||||
hx-target="#search-dropdown"
|
hx-target="#search-dropdown"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
hx-include="this"/>
|
hx-include="this"/>
|
||||||
</form>
|
|
||||||
<div id="search-dropdown"
|
<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>
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user