mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
feat: paginated user playlists
This commit is contained in:
parent
38dc4beb44
commit
e7c6813ccb
@ -11,6 +11,7 @@ import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
|||||||
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
||||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||||
import 'package:spotube/components/playlist/playlist_card.dart';
|
import 'package:spotube/components/playlist/playlist_card.dart';
|
||||||
|
import 'package:spotube/components/shared/waypoint.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';
|
||||||
import 'package:spotube/services/queries/queries.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
@ -26,6 +27,12 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
|
|
||||||
final playlistsQuery = useQueries.playlist.ofMine(ref);
|
final playlistsQuery = useQueries.playlist.ofMine(ref);
|
||||||
|
|
||||||
|
final pagePlaylists = useMemoized(
|
||||||
|
() => playlistsQuery.pages
|
||||||
|
.expand((page) => page.items?.toList() ?? <PlaylistSimple>[]),
|
||||||
|
[playlistsQuery.pages],
|
||||||
|
);
|
||||||
|
|
||||||
final likedTracksPlaylist = useMemoized(
|
final likedTracksPlaylist = useMemoized(
|
||||||
() => PlaylistSimple()
|
() => PlaylistSimple()
|
||||||
..name = context.l10n.liked_tracks
|
..name = context.l10n.liked_tracks
|
||||||
@ -48,12 +55,12 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
if (searchText.value.isEmpty) {
|
if (searchText.value.isEmpty) {
|
||||||
return [
|
return [
|
||||||
likedTracksPlaylist,
|
likedTracksPlaylist,
|
||||||
...?playlistsQuery.data,
|
...pagePlaylists,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
likedTracksPlaylist,
|
likedTracksPlaylist,
|
||||||
...?playlistsQuery.data,
|
...pagePlaylists,
|
||||||
]
|
]
|
||||||
.map((e) => (weightedRatio(e.name!, searchText.value), e))
|
.map((e) => (weightedRatio(e.name!, searchText.value), e))
|
||||||
.sorted((a, b) => b.$1.compareTo(a.$1))
|
.sorted((a, b) => b.$1.compareTo(a.$1))
|
||||||
@ -61,9 +68,11 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
.map((e) => e.$2)
|
.map((e) => e.$2)
|
||||||
.toList();
|
.toList();
|
||||||
},
|
},
|
||||||
[playlistsQuery.data, searchText.value],
|
[pagePlaylists, searchText.value],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final controller = useScrollController();
|
||||||
|
|
||||||
if (auth == null) {
|
if (auth == null) {
|
||||||
return const AnonymousFallback();
|
return const AnonymousFallback();
|
||||||
}
|
}
|
||||||
@ -71,7 +80,15 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: playlistsQuery.refresh,
|
onRefresh: playlistsQuery.refresh,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
controller: controller,
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
child: Waypoint(
|
||||||
|
controller: controller,
|
||||||
|
onTouchEdge: () {
|
||||||
|
if (playlistsQuery.hasNextPage) {
|
||||||
|
playlistsQuery.fetchNext();
|
||||||
|
}
|
||||||
|
},
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@ -85,8 +102,8 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
AnimatedCrossFade(
|
AnimatedCrossFade(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
crossFadeState:
|
crossFadeState: playlistsQuery.isLoadingPage ||
|
||||||
playlistsQuery.isLoading || !playlistsQuery.hasData
|
!playlistsQuery.hasPageData
|
||||||
? CrossFadeState.showFirst
|
? CrossFadeState.showFirst
|
||||||
: CrossFadeState.showSecond,
|
: CrossFadeState.showSecond,
|
||||||
firstChild:
|
firstChild:
|
||||||
@ -118,6 +135,7 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:async/async.dart';
|
||||||
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
@ -21,11 +22,33 @@ class PlaylistAddTrackDialog extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final spotify = ref.watch(spotifyProvider);
|
final spotify = ref.watch(spotifyProvider);
|
||||||
final userPlaylists = useQueries.playlist.ofMine(ref);
|
final userPlaylists = useQueries.playlist.ofMine(ref);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
final op = CancelableOperation.fromFuture(
|
||||||
|
() async {
|
||||||
|
while (userPlaylists.hasNextPage) {
|
||||||
|
await userPlaylists.fetchNext();
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return () {
|
||||||
|
op.cancel();
|
||||||
|
};
|
||||||
|
}, [userPlaylists.hasNextPage]);
|
||||||
|
|
||||||
final me = useQueries.user.me(ref);
|
final me = useQueries.user.me(ref);
|
||||||
final filteredPlaylists = userPlaylists.data?.where(
|
|
||||||
|
final filteredPlaylists = useMemoized(
|
||||||
|
() => userPlaylists.pages
|
||||||
|
.expand((page) => page.items?.toList() ?? <PlaylistSimple>[])
|
||||||
|
.where(
|
||||||
(playlist) =>
|
(playlist) =>
|
||||||
playlist.owner?.id != null && playlist.owner!.id == me.data?.id,
|
playlist.owner?.id != null && playlist.owner!.id == me.data?.id,
|
||||||
|
),
|
||||||
|
[userPlaylists.pages, me.data?.id],
|
||||||
);
|
);
|
||||||
|
|
||||||
final playlistsCheck = useState(<String, bool>{});
|
final playlistsCheck = useState(<String, bool>{});
|
||||||
final queryClient = useQueryClient();
|
final queryClient = useQueryClient();
|
||||||
|
|
||||||
@ -70,11 +93,11 @@ class PlaylistAddTrackDialog extends HookConsumerWidget {
|
|||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
height: 300,
|
height: 300,
|
||||||
width: 300,
|
width: 300,
|
||||||
child: !userPlaylists.hasData
|
child: userPlaylists.hasNextPage
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: filteredPlaylists!.length,
|
itemCount: filteredPlaylists.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final playlist = filteredPlaylists.elementAt(index);
|
final playlist = filteredPlaylists.elementAt(index);
|
||||||
return CheckboxListTile(
|
return CheckboxListTile(
|
||||||
|
@ -156,7 +156,6 @@ class PlaylistHeartButton extends HookConsumerWidget {
|
|||||||
playlist.id!,
|
playlist.id!,
|
||||||
refreshQueries: [
|
refreshQueries: [
|
||||||
isLikedQuery.key,
|
isLikedQuery.key,
|
||||||
"current-user-playlists",
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:fl_query/fl_query.dart';
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/hooks/use_spotify_mutation.dart';
|
import 'package:spotube/hooks/use_spotify_mutation.dart';
|
||||||
|
|
||||||
@ -9,7 +10,9 @@ class PlaylistMutations {
|
|||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
String playlistId, {
|
String playlistId, {
|
||||||
List<String>? refreshQueries,
|
List<String>? refreshQueries,
|
||||||
|
List<String>? refreshInfiniteQueries,
|
||||||
}) {
|
}) {
|
||||||
|
final queryClient = useQueryClient();
|
||||||
return useSpotifyMutation<bool, dynamic, bool, dynamic>(
|
return useSpotifyMutation<bool, dynamic, bool, dynamic>(
|
||||||
"toggle-playlist-like/$playlistId",
|
"toggle-playlist-like/$playlistId",
|
||||||
(isLiked, spotify) async {
|
(isLiked, spotify) async {
|
||||||
@ -22,6 +25,11 @@ class PlaylistMutations {
|
|||||||
},
|
},
|
||||||
ref: ref,
|
ref: ref,
|
||||||
refreshQueries: refreshQueries,
|
refreshQueries: refreshQueries,
|
||||||
|
refreshInfiniteQueries: refreshInfiniteQueries,
|
||||||
|
onData: (data, recoveryData) async {
|
||||||
|
await queryClient
|
||||||
|
.refreshInfiniteQueryAllPages("current-user-playlists");
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,8 @@ class AlbumQueries {
|
|||||||
return useSpotifyQuery<bool, dynamic>(
|
return useSpotifyQuery<bool, dynamic>(
|
||||||
"is-saved-for-me/$album",
|
"is-saved-for-me/$album",
|
||||||
(spotify) {
|
(spotify) {
|
||||||
return spotify.me.isSavedAlbums([album]).then((value) => value.first);
|
return spotify.me
|
||||||
|
.containsSavedAlbums([album]).then((value) => value[album]);
|
||||||
},
|
},
|
||||||
ref: ref,
|
ref: ref,
|
||||||
);
|
);
|
||||||
|
@ -126,12 +126,18 @@ class PlaylistQueries {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Query<Iterable<PlaylistSimple>, dynamic> ofMine(WidgetRef ref) {
|
InfiniteQuery<Page<PlaylistSimple>, dynamic, int> ofMine(WidgetRef ref) {
|
||||||
return useSpotifyQuery<Iterable<PlaylistSimple>, dynamic>(
|
return useSpotifyInfiniteQuery<Page<PlaylistSimple>, dynamic, int>(
|
||||||
"current-user-playlists",
|
"current-user-playlists",
|
||||||
(spotify) {
|
(page, spotify) async {
|
||||||
return spotify.playlists.me.all();
|
final playlists = await spotify.playlists.me.getPage(10, page * 10);
|
||||||
|
return playlists;
|
||||||
},
|
},
|
||||||
|
initialPage: 0,
|
||||||
|
nextPage: (lastPage, lastPageData) =>
|
||||||
|
(lastPageData.items?.length ?? 0) < 10 || lastPageData.isLast
|
||||||
|
? null
|
||||||
|
: lastPage + 1,
|
||||||
ref: ref,
|
ref: ref,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user