Files
minstrel/flutter_client/lib/models/page.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

43 lines
1.3 KiB
Dart

// Mirrors the server's Page<T> envelope used by paged endpoints
// (/api/search, /api/library/artists, /api/library/albums,
// /api/library/history, /api/me/likes/*).
//
// Wire shape from internal/api/types.go Page[T]:
// { items: T[], total: int, limit: int, offset: int }
//
// Dart generics can't auto-infer T from JSON, so callers pass an
// itemFromJson function alongside the raw map. Parse logic stays in
// each entity's own fromJson; this class only handles the envelope.
class Page<T> {
const Page({
required this.items,
required this.total,
required this.limit,
required this.offset,
});
final List<T> items;
final int total;
final int limit;
final int offset;
factory Page.fromJson(
Map<String, dynamic> j,
T Function(Map<String, dynamic>) itemFromJson,
) {
final raw = (j['items'] as List?) ?? const [];
return Page<T>(
items: raw
.map((e) => itemFromJson((e as Map).cast<String, dynamic>()))
.toList(growable: false),
total: (j['total'] as num?)?.toInt() ?? 0,
limit: (j['limit'] as num?)?.toInt() ?? 0,
offset: (j['offset'] as num?)?.toInt() ?? 0,
);
}
/// Convenience for endpoints that always return the first page or
/// when the caller just wants the items.
bool get hasMore => offset + items.length < total;
}