feat: newly released albums of user followed artist

This commit is contained in:
Kingkor Roy Tirtho 2023-05-11 23:50:17 +06:00
parent 38929fed6e
commit 33cb7947d6
4 changed files with 65 additions and 45 deletions

View File

@ -4,10 +4,8 @@ import 'package:collection/collection.dart';
import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart'; import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
import 'package:spotube/components/shared/waypoint.dart';
import 'package:spotube/components/artist/artist_card.dart'; import 'package:spotube/components/artist/artist_card.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
@ -22,17 +20,12 @@ class UserArtists extends HookConsumerWidget {
final theme = Theme.of(context); final theme = Theme.of(context);
final auth = ref.watch(AuthenticationNotifier.provider); final auth = ref.watch(AuthenticationNotifier.provider);
final artistQuery = useQueries.artist.followedByMe(ref); final artistQuery = useQueries.artist.followedByMeAll(ref);
final hasNextPage = artistQuery.pages.isEmpty
? false
: (artistQuery.pages.last.items?.length ?? 0) == 15;
final searchText = useState(''); final searchText = useState('');
final filteredArtists = useMemoized(() { final filteredArtists = useMemoized(() {
final artists = artistQuery.pages final artists = artistQuery.data ?? [];
.expand<Artist>((page) => page.items ?? const Iterable.empty());
if (searchText.value.isEmpty) { if (searchText.value.isEmpty) {
return artists.toList(); return artists.toList();
@ -46,7 +39,7 @@ class UserArtists extends HookConsumerWidget {
.where((e) => e.item1 > 50) .where((e) => e.item1 > 50)
.map((e) => e.item2) .map((e) => e.item2)
.toList(); .toList();
}, [artistQuery.pages, searchText.value]); }, [artistQuery.data, searchText.value]);
final controller = useScrollController(); final controller = useScrollController();
@ -72,7 +65,7 @@ class UserArtists extends HookConsumerWidget {
), ),
), ),
backgroundColor: theme.scaffoldBackgroundColor, backgroundColor: theme.scaffoldBackgroundColor,
body: artistQuery.pages.isEmpty body: artistQuery.data?.isEmpty == true
? Padding( ? Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Row( child: Row(
@ -86,7 +79,7 @@ class UserArtists extends HookConsumerWidget {
) )
: RefreshIndicator( : RefreshIndicator(
onRefresh: () async { onRefresh: () async {
await artistQuery.refreshAll(); await artistQuery.refresh();
}, },
child: SingleChildScrollView( child: SingleChildScrollView(
controller: controller, controller: controller,
@ -97,18 +90,9 @@ class UserArtists extends HookConsumerWidget {
child: Wrap( child: Wrap(
spacing: 15, spacing: 15,
runSpacing: 5, runSpacing: 5,
children: filteredArtists.mapIndexed((index, artist) { children: filteredArtists
if (index == artistQuery.pages.length - 1 && .mapIndexed((index, artist) => ArtistCard(artist))
hasNextPage) { .toList(),
return Waypoint(
controller: controller,
isGrid: true,
onTouchEdge: artistQuery.fetchNext,
child: ArtistCard(artist),
);
}
return ArtistCard(artist);
}).toList(),
), ),
), ),
), ),

View File

@ -14,8 +14,8 @@ import 'package:spotube/services/queries/queries.dart';
import 'package:spotube/utils/type_conversion_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart';
class PersonalizedItemCard extends HookWidget { class PersonalizedItemCard extends HookWidget {
final Iterable<Page<PlaylistSimple>>? playlists; final Iterable<PlaylistSimple>? playlists;
final Iterable<Page<AlbumSimple>>? albums; final Iterable<AlbumSimple>? albums;
final String title; final String title;
final bool hasNextPage; final bool hasNextPage;
final void Function() onFetchMore; final void Function() onFetchMore;
@ -36,18 +36,6 @@ class PersonalizedItemCard extends HookWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final scrollController = useScrollController(); final scrollController = useScrollController();
final playlistItems = playlists
?.expand(
(page) => page.items ?? const Iterable<PlaylistSimple>.empty(),
)
.toList();
final albumItems = albums
?.expand(
(page) => page.items ?? const Iterable<AlbumSimple>.empty(),
)
.toList();
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
@ -82,9 +70,8 @@ class PersonalizedItemCard extends HookWidget {
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
...?playlistItems ...?playlists?.map((playlist) => PlaylistCard(playlist)),
?.map((playlist) => PlaylistCard(playlist)), ...?albums?.map(
...?albumItems?.map(
(album) => AlbumCard( (album) => AlbumCard(
TypeConversionUtils.simpleAlbum_X_Album(album), TypeConversionUtils.simpleAlbum_X_Album(album),
), ),
@ -108,20 +95,43 @@ class PersonalizedPage extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final featuredPlaylistsQuery = useQueries.playlist.featured(ref); final featuredPlaylistsQuery = useQueries.playlist.featured(ref);
final playlists = useMemoized(
() => featuredPlaylistsQuery.pages
.whereType<Page<PlaylistSimple>>()
.expand((page) => page.items ?? const <PlaylistSimple>[]),
[featuredPlaylistsQuery.pages],
);
final newReleases = useQueries.album.newReleases(ref); final newReleases = useQueries.album.newReleases(ref);
final userArtists = useQueries.artist
.followedByMeAll(ref)
.data
?.map((s) => s.id!)
.toList() ??
const [];
final albums = useMemoized(
() => newReleases.pages
.whereType<Page<AlbumSimple>>()
.expand((page) => page.items ?? const <AlbumSimple>[])
.where((album) {
return album.artists
?.any((artist) => userArtists.contains(artist.id!)) ==
true;
}),
[newReleases.pages],
);
return ListView( return ListView(
children: [ children: [
PersonalizedItemCard( PersonalizedItemCard(
playlists: playlists: playlists,
featuredPlaylistsQuery.pages.whereType<Page<PlaylistSimple>>(),
title: context.l10n.featured, title: context.l10n.featured,
hasNextPage: featuredPlaylistsQuery.hasNextPage, hasNextPage: featuredPlaylistsQuery.hasNextPage,
onFetchMore: featuredPlaylistsQuery.fetchNext, onFetchMore: featuredPlaylistsQuery.fetchNext,
), ),
PersonalizedItemCard( PersonalizedItemCard(
albums: newReleases.pages.whereType<Page<AlbumSimple>>(), albums: albums,
title: context.l10n.new_releases, title: context.l10n.new_releases,
hasNextPage: newReleases.hasNextPage, hasNextPage: newReleases.hasNextPage,
onFetchMore: newReleases.fetchNext, onFetchMore: newReleases.fetchNext,

View File

@ -58,7 +58,7 @@ class AlbumQueries {
try { try {
final albums = await spotify.browse final albums = await spotify.browse
.getNewReleases(country: market) .getNewReleases(country: market)
.getPage(5, pageParam); .getPage(50, pageParam);
return albums; return albums;
} catch (e, stack) { } catch (e, stack) {

View File

@ -38,6 +38,32 @@ class ArtistQueries {
); );
} }
Query<List<Artist>, dynamic> followedByMeAll(WidgetRef ref) {
return useSpotifyQuery(
"user-following-artists-all",
(spotify) async {
CursorPage<Artist>? page =
await spotify.me.following(FollowingType.artist).getPage(50);
final following = <Artist>[];
if (page.isLast == true) {
return page.items?.toList() ?? [];
}
while (page?.isLast != true) {
following.addAll(page?.items ?? []);
page = await spotify.me
.following(FollowingType.artist)
.getPage(50, page?.after ?? '');
}
return following;
},
ref: ref,
);
}
Query<bool, dynamic> doIFollow( Query<bool, dynamic> doIFollow(
WidgetRef ref, WidgetRef ref,
String artist, String artist,