mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
feat: newly released albums of user followed artist
This commit is contained in:
parent
38929fed6e
commit
33cb7947d6
@ -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(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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,
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user