feat: the Wanted view becomes Collection, shaped like Lidarr: an artist catalogue first, the state sections down a left rail (M502 #5577)
release / govulncheck (push) Successful in 21s
release / web (push) Successful in 1m39s
release / go (push) Successful in 1m54s
release / integration (push) Successful in 5m33s
release / android (push) Successful in 5m40s
release / Build signed APK (releases and dev) (push) Successful in 5m56s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 21s
release / web (push) Successful in 1m39s
release / go (push) Successful in 1m54s
release / integration (push) Successful in 5m33s
release / android (push) Successful in 5m40s
release / Build signed APK (releases and dev) (push) Successful in 5m56s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
The section opens on a catalogue of every artist on the list, A–Z with a letter rail, each card showing how many of their albums are held. An artist opens on their page: every album of theirs, held or not, with its status, search, releases and tracks. Wanted, Missing files, Activity, Following and Blocked sit down a left rail as the sections that show state; Held is gone, the catalogue shows it. The section moves from /wanted to /collection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,7 @@ const countWants = `-- name: CountWants :one
|
||||
SELECT COUNT(*) FILTER (WHERE fulfilled_at IS NULL AND reason <> 'missing')::bigint AS wanted,
|
||||
COUNT(*) FILTER (WHERE fulfilled_at IS NULL AND reason = 'missing')::bigint AS missing,
|
||||
COUNT(*) FILTER (WHERE fulfilled_at IS NOT NULL)::bigint AS held,
|
||||
COUNT(DISTINCT artist_mbid)::bigint AS artists,
|
||||
(SELECT COUNT(*) FROM follows)::bigint AS following,
|
||||
-- The Wanted view's other tabs (#5522): downloads running, those
|
||||
-- waiting on a person, and releases set aside.
|
||||
@@ -52,6 +53,7 @@ type CountWantsRow struct {
|
||||
Wanted int64
|
||||
Missing int64
|
||||
Held int64
|
||||
Artists int64
|
||||
Following int64
|
||||
Activity int64
|
||||
Attention int64
|
||||
@@ -65,6 +67,7 @@ func (q *Queries) CountWants(ctx context.Context) (CountWantsRow, error) {
|
||||
&i.Wanted,
|
||||
&i.Missing,
|
||||
&i.Held,
|
||||
&i.Artists,
|
||||
&i.Following,
|
||||
&i.Activity,
|
||||
&i.Attention,
|
||||
@@ -852,6 +855,98 @@ func (q *Queries) ListUnmatchedLibraryAlbums(ctx context.Context, maxRows int32)
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listWantArtists = `-- name: ListWantArtists :many
|
||||
WITH a AS (
|
||||
SELECT w.artist_mbid,
|
||||
(array_agg(w.artist_name ORDER BY w.wanted_at DESC))[1] AS artist_name,
|
||||
COUNT(*) FILTER (WHERE w.fulfilled_at IS NOT NULL)::int AS held,
|
||||
COUNT(*) FILTER (WHERE w.fulfilled_at IS NULL AND w.reason <> 'missing')::int AS wanted,
|
||||
COUNT(*) FILTER (WHERE w.fulfilled_at IS NULL AND w.reason = 'missing')::int AS missing,
|
||||
COUNT(*)::int AS albums,
|
||||
bool_or($1::text <> '' AND strpos(lower(w.title), lower($1::text)) > 0) AS title_match
|
||||
FROM wants w
|
||||
WHERE $4::text = '' OR w.artist_mbid = $4::text
|
||||
GROUP BY w.artist_mbid
|
||||
)
|
||||
SELECT a.artist_mbid, a.artist_name::text AS artist_name, a.held, a.wanted, a.missing, a.albums,
|
||||
COALESCE(NULLIF(la.sort_name, ''), a.artist_name)::text AS sort_name,
|
||||
la.id AS library_artist_id,
|
||||
(f.artist_mbid IS NOT NULL)::bool AS following,
|
||||
(SELECT COUNT(*) FROM grabs g JOIN wants x ON x.id = g.want_id
|
||||
WHERE x.artist_mbid = a.artist_mbid AND g.ended_at IS NULL)::int AS downloading,
|
||||
COUNT(*) OVER () AS total
|
||||
FROM a
|
||||
LEFT JOIN LATERAL (SELECT ar.id, ar.sort_name FROM artists ar WHERE ar.mbid = a.artist_mbid LIMIT 1) la ON true
|
||||
LEFT JOIN follows f ON f.artist_mbid = a.artist_mbid
|
||||
WHERE $1::text = ''
|
||||
OR strpos(lower(a.artist_name), lower($1::text)) > 0
|
||||
OR a.title_match
|
||||
ORDER BY lower(COALESCE(NULLIF(la.sort_name, ''), a.artist_name)), a.artist_mbid
|
||||
LIMIT $3 OFFSET $2
|
||||
`
|
||||
|
||||
type ListWantArtistsParams struct {
|
||||
Q string
|
||||
Skip int32
|
||||
MaxRows int32
|
||||
ArtistMbid string
|
||||
}
|
||||
|
||||
type ListWantArtistsRow struct {
|
||||
ArtistMbid string
|
||||
ArtistName string
|
||||
Held int32
|
||||
Wanted int32
|
||||
Missing int32
|
||||
Albums int32
|
||||
SortName string
|
||||
LibraryArtistID pgtype.UUID
|
||||
Following bool
|
||||
Downloading int32
|
||||
Total int64
|
||||
}
|
||||
|
||||
// The Wanted view's main page (#5572): every artist on the list, A–Z like
|
||||
// the library, with how many of their albums are held of how many are on
|
||||
// the list. q matches the artist's name or any of their albums' titles;
|
||||
// artist_mbid narrows to one artist, for their page's header.
|
||||
func (q *Queries) ListWantArtists(ctx context.Context, arg ListWantArtistsParams) ([]ListWantArtistsRow, error) {
|
||||
rows, err := q.db.Query(ctx, listWantArtists,
|
||||
arg.Q,
|
||||
arg.Skip,
|
||||
arg.MaxRows,
|
||||
arg.ArtistMbid,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []ListWantArtistsRow
|
||||
for rows.Next() {
|
||||
var i ListWantArtistsRow
|
||||
if err := rows.Scan(
|
||||
&i.ArtistMbid,
|
||||
&i.ArtistName,
|
||||
&i.Held,
|
||||
&i.Wanted,
|
||||
&i.Missing,
|
||||
&i.Albums,
|
||||
&i.SortName,
|
||||
&i.LibraryArtistID,
|
||||
&i.Following,
|
||||
&i.Downloading,
|
||||
&i.Total,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listWants = `-- name: ListWants :many
|
||||
SELECT w.id, w.release_group_mbid, w.artist_mbid, w.artist_name, w.title, w.reason,
|
||||
w.album_id, w.wanted_at, w.fulfilled_at,
|
||||
@@ -879,24 +974,27 @@ SELECT w.id, w.release_group_mbid, w.artist_mbid, w.artist_name, w.title, w.reas
|
||||
LEFT JOIN grabs gr ON gr.id = (SELECT x.id FROM grabs x WHERE x.want_id = w.id
|
||||
ORDER BY x.ended_at IS NULL DESC, x.grabbed_at DESC LIMIT 1)
|
||||
WHERE CASE $1::text
|
||||
WHEN 'all' THEN true
|
||||
WHEN 'held' THEN w.fulfilled_at IS NOT NULL
|
||||
WHEN 'missing' THEN w.fulfilled_at IS NULL AND w.reason = 'missing'
|
||||
ELSE w.fulfilled_at IS NULL AND w.reason <> 'missing'
|
||||
END
|
||||
AND ($2::text = ''
|
||||
OR strpos(lower(w.artist_name), lower($2::text)) > 0
|
||||
OR strpos(lower(w.title), lower($2::text)) > 0)
|
||||
AND ($2::text = '' OR w.artist_mbid = $2::text)
|
||||
AND ($3::text = ''
|
||||
OR strpos(lower(w.artist_name), lower($3::text)) > 0
|
||||
OR strpos(lower(w.title), lower($3::text)) > 0)
|
||||
ORDER BY lower(COALESCE(NULLIF(la.sort_name, ''), w.artist_name)), w.artist_mbid,
|
||||
NULLIF(COALESCE(NULLIF(w.first_release_date, ''), g.first_release_date, ''), '') NULLS LAST,
|
||||
lower(w.title), w.id
|
||||
LIMIT $4 OFFSET $3
|
||||
LIMIT $5 OFFSET $4
|
||||
`
|
||||
|
||||
type ListWantsParams struct {
|
||||
State string
|
||||
Q string
|
||||
Skip int32
|
||||
MaxRows int32
|
||||
State string
|
||||
ArtistMbid string
|
||||
Q string
|
||||
Skip int32
|
||||
MaxRows int32
|
||||
}
|
||||
|
||||
type ListWantsRow struct {
|
||||
@@ -937,13 +1035,14 @@ type ListWantsRow struct {
|
||||
}
|
||||
|
||||
// The Wanted page: open wants (all but missing ones, or only missing ones),
|
||||
// or the held ones. Ordered like the library (#5572): artists A–Z by the
|
||||
// the held ones, or all of them; artist_mbid narrows to one artist's page. Ordered like the library (#5572): artists A–Z by the
|
||||
// library's sort name when it holds the artist, each artist's albums by
|
||||
// release date, so the page groups them as they arrive. q narrows to an
|
||||
// artist or album title; total counts every match, past the page.
|
||||
func (q *Queries) ListWants(ctx context.Context, arg ListWantsParams) ([]ListWantsRow, error) {
|
||||
rows, err := q.db.Query(ctx, listWants,
|
||||
arg.State,
|
||||
arg.ArtistMbid,
|
||||
arg.Q,
|
||||
arg.Skip,
|
||||
arg.MaxRows,
|
||||
|
||||
Reference in New Issue
Block a user