Files
minstrel/flutter_client
bvandeusen bb9e979876 feat(web,flutter): #401 — now-playing highlight on TrackRow + PlaylistTrackRow
Cross-platform consistency: when a track is currently playing, its row
on the album / liked / history / search / playlist detail surfaces
gets the same accent-border + bg-lift treatment that QueueTrackRow
applies on the queue panel. Closes the inconsistency caught during
the #375 DRY audit (the queue row had a "you are here" indicator;
other track-row surfaces did not).

Web — TrackRow.svelte + PlaylistTrackRow.svelte:
  isCurrent = player.current?.id === track.id
  → border-l-2 border-l-accent bg-surface-hover when true.
  PlaylistTrackRow additionally gates on !isUnavailable so deleted
  rows never match a phantom playing id.

Flutter — TrackRow + _PlaylistTrackRow:
  TrackRow becomes a ConsumerWidget, watches mediaItemProvider, and
  wraps its InkWell in a Container whose BoxDecoration carries the
  fs.iron background + 2px fs.accent left border when current. Title
  text also shifts to fs.accent and FontWeight.w500. Same pattern for
  _PlaylistTrackRow.

No "Now playing" pill on these surfaces — the player bar already
names the track, so the accent band alone reads as enough cue.

For #401 / #356 umbrella.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:05:33 -04:00
..

Minstrel mobile client

Flutter (iOS + Android) sibling of the SvelteKit web SPA. v1 first slice ships: scaffold, auth, library browse (home / artist / album), likes, player with background audio + lock-screen controls.

Setup

cd flutter_client
flutter pub get
./tool/sync_shared.sh         # copy tokens / error-copy / placeholders from ../web
dart run tool/gen_tokens.dart # regen lib/theme/tokens.dart from shared/fabledsword.tokens.json
flutter run

sync_shared.sh is idempotent. CI runs it on every build; rerun locally whenever web/src/lib/styles/tokens.json, web/src/lib/styles/error-copy.json, or web/static/placeholders/album-fallback.svg changes.

Project layout

See docs/superpowers/specs/2026-05-02-flutter-mobile-foundation-design.md section 3. Briefly:

  • lib/api/ — dio client, ApiError, error-copy loader, per-surface endpoints.
  • lib/auth/ — server URL screen, login screen, AuthController + secure storage.
  • lib/library/ — home, artist detail, album detail, providers, widgets.
  • lib/likes/ — LikeButton + optimistic toggle controller.
  • lib/player/ — audio handler, player provider, mini PlayerBar, NowPlaying.
  • lib/theme/ — FabledSword token Dart class (generated), ThemeExtension, ThemeData.
  • lib/shared/ — routing (go_router shell), version gate, connection error banner.

shared/fabledsword.tokens.json and assets/error-copy.json are synced from web/. Don't edit them directly — edit web/src/lib/styles/ and re-run ./tool/sync_shared.sh.

Architecture

  • State: Riverpod 2 (AsyncValue, AsyncNotifier, family providers).
  • HTTP: dio 5 with a Bearer-auth interceptor; 401 clears the session and the router redirects to login.
  • Audio: just_audio wrapped by audio_service for background playback + lock-screen / notification controls.
  • Auth: Bearer tokens in flutter_secure_storage. Server already supports both cookie (web SPA) and Bearer (internal/auth/session.go).
  • Routing: go_router with a ShellRoute so the PlayerBar persists across navigation. Cold launch flow: no server-url → /server-url, url set / no token → /login, token present → /home.

CI

.forgejo/workflows/flutter.yml runs on push to dev/main, tag pushes, PR to main, and manual dispatch — path-filtered to flutter_client/** plus the shared web inputs. Steps: sync, analyze, test, build APK. Debug APKs upload as artifacts; release APKs attach to the Forgejo release on tag.

Versioning

pubspec.yaml version mirrors the server tag (e.g. server v0.1.0 → Flutter 0.1.0+1). The server's /healthz returns min_client_version; old clients show an Update Required modal and refuse to operate. Bump internal/server/version.go MinClientVersion when a server-side change requires a paired client update.

Distribution

  • Android: APK on the Forgejo release page. No Play Store for v1.
  • iOS: TestFlight on tag (manual upload v1, automate later).

Out-of-scope for slice 1

Search, discover, requests, settings beyond server URL, admin, listening history, playlists, offline cache (#357). Those land in subsequent slices with their own brainstorm/spec/plan cycles. Per the operator's M7 ordering decision (2026-05-02), slice 2+ pauses to ship the missing web features first (#352 playlists, #362 theme toggle, #363 queue UI, #364 listening history, etc.) before mirroring on Flutter.