diff --git a/flutter_client/lib/api/endpoints/playlists.dart b/flutter_client/lib/api/endpoints/playlists.dart index 6019f858..fc4bac55 100644 --- a/flutter_client/lib/api/endpoints/playlists.dart +++ b/flutter_client/lib/api/endpoints/playlists.dart @@ -2,23 +2,42 @@ import 'package:dio/dio.dart'; import '../../models/playlist.dart'; +/// Wire shape for GET /api/playlists. Server splits owned vs. public so +/// the UI can present them as different sections; we preserve that split +/// to give the integrations page room to grow. +class PlaylistsList { + const PlaylistsList({required this.owned, required this.public}); + final List owned; + final List public; + + factory PlaylistsList.empty() => + const PlaylistsList(owned: [], public: []); + + /// Concatenated view for callers that don't care about the split. + List get all => [...owned, ...public]; +} + class PlaylistsApi { PlaylistsApi(this._dio); final Dio _dio; - /// GET /api/playlists?kind=user|system|all. The server's default kind - /// is "user" — passing "all" returns user playlists + system mixes. - /// "system" returns just for-you / discover. The mobile UI defaults - /// to "all" so users see their generated mixes alongside their own. - Future> list({String kind = 'all'}) async { - final r = await _dio.get>( + /// GET /api/playlists?kind=user|system|all. Server returns + /// `{"owned": [...], "public": [...]}`. Owned is the caller's own + /// playlists, filtered by the kind param (default "user"). Public + /// is other users' shared playlists; not filtered by kind. + Future list({String kind = 'user'}) async { + final r = await _dio.get>( '/api/playlists', queryParameters: {'kind': kind}, ); - final raw = r.data ?? const []; - return raw - .map((e) => Playlist.fromJson((e as Map).cast())) - .toList(growable: false); + final body = r.data ?? const {}; + List parse(String key) { + final raw = (body[key] as List?) ?? const []; + return raw + .map((e) => Playlist.fromJson((e as Map).cast())) + .toList(growable: false); + } + return PlaylistsList(owned: parse('owned'), public: parse('public')); } Future get(String id) async { diff --git a/flutter_client/lib/playlists/playlists_list_screen.dart b/flutter_client/lib/playlists/playlists_list_screen.dart index 55614b93..217599b6 100644 --- a/flutter_client/lib/playlists/playlists_list_screen.dart +++ b/flutter_client/lib/playlists/playlists_list_screen.dart @@ -31,7 +31,8 @@ class PlaylistsListScreen extends ConsumerWidget { error: (e, _) => Center( child: Text('$e', style: TextStyle(color: fs.error)), ), - data: (items) { + data: (lists) { + final items = lists.all; if (items.isEmpty) { return Center( child: Text( diff --git a/flutter_client/lib/playlists/playlists_provider.dart b/flutter_client/lib/playlists/playlists_provider.dart index fb0c4306..cf8e4fe4 100644 --- a/flutter_client/lib/playlists/playlists_provider.dart +++ b/flutter_client/lib/playlists/playlists_provider.dart @@ -9,7 +9,7 @@ final playlistsApiProvider = FutureProvider((ref) async { }); final playlistsListProvider = - FutureProvider.family, String>((ref, kind) async { + FutureProvider.family((ref, kind) async { return (await ref.watch(playlistsApiProvider.future)).list(kind: kind); });