147d6e280e
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
39 lines
1.2 KiB
Dart
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;
|
|
}
|