Closes the cleanest remaining #356 gap — Discover submits Lidarr
requests but had no surface for users to view or cancel their own.
Admin had it; user didn't.
- RequestsApi at lib/api/endpoints/requests.dart — listMine() +
cancel(id). Same /api/requests endpoint the web /requests page
uses; identical AdminRequest wire shape so the existing model
is reused (just augmented with matched_*_id fields for the
"Listen" CTA on completed rows).
- MyRequestsController mirrors the web's auto-poll (#369 piece
already shipped server-side / web-side): 12s refresh while any
row is mid-ingest (status='approved'), stops on settle. Riverpod
Timer in build() that's cancelled in onDispose.
- RequestsScreen with kind/status pills, ingest progress copy,
Cancel (with confirm dialog) and Listen CTAs per row state.
- Route /requests under the shell. Entry point in settings between
Profile and Appearance — "My requests".
- Widget tests cover empty / pending / completed / rejected states +
the Cancel-confirm dialog.
Out of scope this slice: Discover-screen "View your requests" CTA
after submitting a new request. Reasonable follow-up but doesn't
block the management loop.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three classes:
1. album_cover_cache.dart docstring used <applicationCacheDirectory>
and <albumId> which the analyzer reads as unintended HTML. Wrapped
the path in backticks and used {} placeholders.
2. settings AppearanceSection used RadioListTile.groupValue + onChanged,
deprecated in Flutter 3.32+. Wrapped the radios in a RadioGroup
ancestor (the new API) so individual tiles only declare value +
activeColor. Test updated to read groupValue off the RadioGroup
ancestor.
3. theme_extension.dart factories built FabledSwordTheme(...) without
const, even though all args are static const tokens. Added const to
the outer constructor calls in both .dark() and .light() — this
propagates const to the inner TextStyle(...) calls automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three RadioListTiles between Profile and Password sections. Tap →
themeModeProvider.notifier.set(...) → MaterialApp rebuilds with the
new theme via the existing themeMode wiring.
System has the only subtitle ("Match the device setting") — the
light/dark options are self-explanatory.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI caught six issues against the new admin slice:
- AsyncValue<T> in this Riverpod 3.3.1 codebase exposes `.value`
(returns T?), not `.valueOrNull`. Switched the three admin readers
to match the established convention (player_bar, queue_screen,
now_playing_screen all use `.value`).
- home_screen.dart still has ctx.push calls in _albumsRow / _artistsRow
(carousel tap handlers) — restored the go_router import that
Task 2 over-eagerly removed.
- admin_user_edit_sheet.dart had a single-line `if (!await ...) return;`
that violated curly_braces_in_flow_control_structures. Wrapped in
braces.
- admin_quarantine_item.dart doc comment had `<top>` placeholders that
the analyzer flagged as unintended HTML. Rephrased without angle
brackets.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Settings gains an _AdminSection that renders an "Admin" tile (Shield
icon → /admin) only when the current profile has is_admin = true.
Non-admins see the section as SizedBox.shrink() so the const ListView
children stay const.
Provides a discoverable second entry to the admin landing alongside
the kebab overflow shortcut.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Search keeps its conditional clear-text button and gains the shared
nav widget after it. Discover/Playlists/Settings get an actions row
they didn't have before. The kebab is now reachable from every
top-level screen, which is the prerequisite for the Admin entry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>