From 3fe9d0a6127a9d7099eefba43044f86302071296 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 13 Sep 2026 22:38:27 -0400 Subject: [PATCH] fix: the Latest feed's filter dropdowns opened empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Operator: "the filters in the latest feed, drop down but don't have values". Two separate causes: - Platform: PostsFilterBar built its items from `platformsStore.platforms`. The platforms store has never had that property; it exposes `list` and `byKey`. The read returned undefined, `|| []` turned that into an empty list, and nothing failed. ArtistsView had copied the same read, so the Browse → Artists platform filter was empty too. Both now read `list` and show platform names rather than raw keys. - Artist: the autocomplete searched the server only after something was typed (autocomplete returns [] for an empty query by design, which its tests pin). Opening the dropdown therefore showed an empty menu. PostsFilterBar now loads every artist once from a new lightweight `GET /api/artists/names` (id, name, slug; alphabetical; no joins) and filters client-side, so the list is there on open. A deep-linked artist_id now also shows the artist's real name instead of "Artist #id". Guard: frontend/test/storeUsage.spec.js scans src for `platformsStore.` and fails on any name the store doesn't define, since the frontend CI has no type-checker to catch this. A positive control shows the shipped `platformsStore.platforms` read is flagged, and a vacuity check confirms the scan really walks the tree. tests/test_api_artists_create.py covers /names. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SHQB1YukL3VyvMK8rcbmV9 --- backend/app/api/artists.py | 10 +++ backend/app/services/artist_service.py | 12 +++ .../src/components/posts/PostsFilterBar.vue | 90 ++++++++----------- frontend/src/views/ArtistsView.vue | 5 +- frontend/test/storeUsage.spec.js | 63 +++++++++++++ tests/test_api_artists_create.py | 13 +++ 6 files changed, 140 insertions(+), 53 deletions(-) create mode 100644 frontend/test/storeUsage.spec.js diff --git a/backend/app/api/artists.py b/backend/app/api/artists.py index 939fe4f..7b41a6a 100644 --- a/backend/app/api/artists.py +++ b/backend/app/api/artists.py @@ -65,6 +65,16 @@ async def autocomplete(): ]) +@artists_bp.route("/names", methods=["GET"]) +async def names(): + """Every artist, id + name + slug, alphabetical. For filter pickers that + list artists before anything is typed; `autocomplete` deliberately returns + nothing for an empty query.""" + async with get_session() as session: + rows = await ArtistService(session).all_names() + return jsonify([{"id": i, "name": n, "slug": s} for i, n, s in rows]) + + @artists_bp.route("/directory", methods=["GET"]) async def directory(): """FC-3f: cursor-paginated artists directory. diff --git a/backend/app/services/artist_service.py b/backend/app/services/artist_service.py index a4150c8..b7de9dc 100644 --- a/backend/app/services/artist_service.py +++ b/backend/app/services/artist_service.py @@ -300,6 +300,18 @@ class ArtistService: await self.session.commit() return artist + async def all_names(self) -> list[tuple[int, str, str]]: + """Every artist as (id, name, slug), alphabetical. + + For pickers that should show a full list before anything is typed (the + Latest feed's artist filter). Three columns and no joins, so it stays + cheap on a library of thousands of artists. + """ + rows = (await self.session.execute( + select(Artist.id, Artist.name, Artist.slug).order_by(func.lower(Artist.name)) + )).all() + return [(r.id, r.name, r.slug) for r in rows] + async def autocomplete(self, prefix: str, limit: int = 20) -> list[Artist]: cleaned = (prefix or "").strip() if not cleaned: diff --git a/frontend/src/components/posts/PostsFilterBar.vue b/frontend/src/components/posts/PostsFilterBar.vue index d7639b1..92c4884 100644 --- a/frontend/src/components/posts/PostsFilterBar.vue +++ b/frontend/src/components/posts/PostsFilterBar.vue @@ -1,21 +1,22 @@