Desktop: the Artists view can only sort by name #32

Open
opened 2026-08-18 05:56:10 +00:00 by logan · 1 comment
Collaborator

Report

Name is the only sort on the Artists page. Are there other useful keys?

Findings

  • frontend/src/components/artists-view/artists-view.ts says so in a comment (~line 196): "There is only one key to sort by" — ARTIST_SORT_OPTIONS has a single entry and sort-field is hardcoded to name.
  • library.Artist (backend/library/query.go:53) carries only ID, Name, MBID and three image paths. Every other key needs the query to return it.

Direction

Candidate keys, all cheap from audio_files joins: album count, track count, total duration, most recently played, most played, and date added (which is a separate request across the app). Extend library.Artist and GetAllArtists... to carry the counts, then add the options. Sorting is done in the frontend today (asc keeps the backend's order, desc reverses) — a new key means either sorting in Go or a real comparator per key; pick one deliberately, since the artists grid is virtualized and its per-render arrow functions are load-bearing for repaint.

**Report** Name is the only sort on the Artists page. Are there other useful keys? **Findings** - `frontend/src/components/artists-view/artists-view.ts` says so in a comment (~line 196): "There is only one key to sort by" — `ARTIST_SORT_OPTIONS` has a single entry and `sort-field` is hardcoded to `name`. - `library.Artist` (`backend/library/query.go:53`) carries only ID, Name, MBID and three image paths. Every other key needs the query to return it. **Direction** Candidate keys, all cheap from `audio_files` joins: album count, track count, total duration, most recently played, most played, and **date added** (which is a separate request across the app). Extend `library.Artist` and `GetAllArtists...` to carry the counts, then add the options. Sorting is done in the frontend today (`asc` keeps the backend's order, `desc` reverses) — a new key means either sorting in Go or a real comparator per key; pick one deliberately, since the artists grid is virtualized and its per-render arrow functions are load-bearing for repaint.
Owner

Scope agreed for this issue and #46, recorded here so it is on the
tracker rather than in a session.

The two are one backend change: GetAlbumArtists selects a.id, a.name, a.mbid and library.Artist carries those plus three image paths, so
every key either issue proposes needs the same projection widened.

The model to follow already exists — cover-grid's.
AlbumSortOption is structurally SortOption & { comparator }, and
page-header only reads id and label, so one array is both the menu
and the sort logic and they cannot drift. genres-view's inline ternary
and artists-view's single degenerate key collapse into it at ~15 lines
each. track-list keeps COLUMN_DEFS: its comparators are keyed by
column, which also carries width, label, phone visibility and a
renderer, and merging the two tables would cost more than it saves.

Sorting stays in the frontend. The new keys need the data from the
backend regardless; they do not need ORDER BY variants. A backend sort
would refetch on every sort change and throw away the array identity
lit-virtualizer keys on — the property card-grid-repaint.test.ts
exists to protect. Adding field to recomputeArtistCaches's memo key
is what makes a comparator model safe there; the copy is per
input-change, not per render.

Keys, by what they cost.

Already over the IPC, pure frontend:

  • Tracks: Last Played. library.Track.LastPlayed is in the struct
    and sent on every track, and there is no lastPlayed entry in
    COLUMN_DEFS. An unused field.
  • Tracks: Favourite. FavoritesController already holds the set
    client-side for the star column.
  • Albums: Release Year (library.Album.ReleaseYear, distinct from
    Year; differs only on remasters — marginal, include only if wanted).

One projection change:

  • Artists: track count, album count, total duration, play count,
    last played
  • Albums: track count, total duration, play count, last played —
    the main grid has three keys today, which is thin
  • Genres: total duration, artist count, album count

Needs audio_files.added_at (#46): date added, everywhere.

Two rollup definitions have to be stated in a comment beside the
query
, the way #46 already asks for date-added's:

  • an album's "last played" is MAX over its tracks;
  • an artist's "play count" is SUM over theirs — which favours a
    prolific artist over a beloved one, and is still right, because the
    question people mean is "who do I listen to most".

Also note home.sql's HomeRecentlyAddedAlbums uses MAX(af.id) as an
explicit stand-in for the missing column and says so; it should move to
added_at in the same pass, and its latest-file semantics should agree
with whatever the album sort key picks.

Split out rather than folded in: album completeness as a key (#184,
blocked on moving the answer out of the per-screenful store) and
recency-weighted play stats (#185). Tag status was considered and
rejected — it would widen the hot track projection to duplicate
autotag-view.

Prerequisite: #138. Sort state is persisted under seven
localStorage keys across five components in three naming conventions,
and nothing resets them between specs. This adds four more, so the
one-module extraction wants doing first.

**Scope agreed for this issue and #46, recorded here so it is on the tracker rather than in a session.** The two are one backend change: `GetAlbumArtists` selects `a.id, a.name, a.mbid` and `library.Artist` carries those plus three image paths, so *every* key either issue proposes needs the same projection widened. **The model to follow already exists — `cover-grid`'s.** `AlbumSortOption` is structurally `SortOption & { comparator }`, and `page-header` only reads `id` and `label`, so one array is both the menu and the sort logic and they cannot drift. `genres-view`'s inline ternary and `artists-view`'s single degenerate key collapse into it at ~15 lines each. `track-list` keeps `COLUMN_DEFS`: its comparators are keyed by *column*, which also carries width, label, phone visibility and a renderer, and merging the two tables would cost more than it saves. **Sorting stays in the frontend.** The new keys need the *data* from the backend regardless; they do not need `ORDER BY` variants. A backend sort would refetch on every sort change and throw away the array identity `lit-virtualizer` keys on — the property `card-grid-repaint.test.ts` exists to protect. Adding `field` to `recomputeArtistCaches`'s memo key is what makes a comparator model safe there; the copy is per input-change, not per render. **Keys, by what they cost.** *Already over the IPC, pure frontend:* - **Tracks: Last Played.** `library.Track.LastPlayed` is in the struct and sent on every track, and there is no `lastPlayed` entry in `COLUMN_DEFS`. An unused field. - **Tracks: Favourite.** `FavoritesController` already holds the set client-side for the star column. - Albums: Release Year (`library.Album.ReleaseYear`, distinct from `Year`; differs only on remasters — marginal, include only if wanted). *One projection change:* - **Artists**: track count, album count, total duration, play count, last played - **Albums**: track count, total duration, play count, last played — the main grid has three keys today, which is thin - **Genres**: total duration, artist count, album count *Needs `audio_files.added_at` (#46):* date added, everywhere. **Two rollup definitions have to be stated in a comment beside the query**, the way #46 already asks for date-added's: - an album's "last played" is `MAX` over its tracks; - an artist's "play count" is `SUM` over theirs — which favours a prolific artist over a beloved one, and is still right, because the question people mean is "who do I listen to most". Also note `home.sql`'s `HomeRecentlyAddedAlbums` uses `MAX(af.id)` as an explicit stand-in for the missing column and says so; it should move to `added_at` in the same pass, and its latest-file semantics should agree with whatever the album sort key picks. **Split out rather than folded in:** album completeness as a key (#184, blocked on moving the answer out of the per-screenful store) and recency-weighted play stats (#185). Tag status was considered and rejected — it would widen the hot track projection to duplicate `autotag-view`. **Prerequisite:** #138. Sort state is persisted under seven `localStorage` keys across five components in three naming conventions, and nothing resets them between specs. This adds four more, so the one-module extraction wants doing first.
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yonlu/yellowjacket#32