Files
minstrel/flutter_client/lib/models/search_response.dart
T
bvandeusen 147d6e280e feat(flutter): bump deps to latest + Search screen slice
Major version bumps (riverpod 2->3, go_router 14->17, just_audio 0.9->0.10,
flutter_secure_storage 9->10, google_fonts 6->8, flutter_lints 4->6).
package_info_plus held at 8.x due to win32 conflict with secure_storage 10.x.

Riverpod 3 breaks: AsyncValue.valueOrNull -> .value, StateProvider replaced
with Notifier subclass. just_audio 0.10: ConcatenatingAudioSource -> setAudioSources.

Search slice:
- models/page.dart (generic Page<T> envelope)
- models/search_response.dart (3-facet wrapper)
- api/endpoints/search.dart
- search/search_provider.dart (debounced 250ms)
- search/search_screen.dart (TextField + 3 horizontal/vertical sections)
- Wired /search route + search button on home AppBar
2026-05-08 14:30:01 -04:00

39 lines
1.2 KiB
Dart

import 'album.dart';
import 'artist.dart';
import 'page.dart';
import 'track.dart';
/// Mirrors internal/api/search.go SearchResponse — three pages keyed by
/// facet, each independently paged. The mobile UI usually only walks
/// the first page of each facet (limit 20-50); infinite scroll within a
/// facet is a future enhancement, not v1.
class SearchResponse {
const SearchResponse({
required this.artists,
required this.albums,
required this.tracks,
});
final Page<ArtistRef> artists;
final Page<AlbumRef> albums;
final Page<TrackRef> tracks;
factory SearchResponse.fromJson(Map<String, dynamic> j) => SearchResponse(
artists: Page.fromJson(
(j['artists'] as Map?)?.cast<String, dynamic>() ?? const {},
ArtistRef.fromJson,
),
albums: Page.fromJson(
(j['albums'] as Map?)?.cast<String, dynamic>() ?? const {},
AlbumRef.fromJson,
),
tracks: Page.fromJson(
(j['tracks'] as Map?)?.cast<String, dynamic>() ?? const {},
TrackRef.fromJson,
),
);
bool get isEmpty =>
artists.items.isEmpty && albums.items.isEmpty && tracks.items.isEmpty;
}