From d2a9ff66525c815d6e9d73c3ff49700ecb3091e5 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sun, 17 Mar 2024 15:21:57 +0600 Subject: [PATCH] feat: use provider in add track dialog --- .../dialogs/playlist_add_track_dialog.dart | 44 +++++++++--------- lib/provider/spotify/playlist/favorite.dart | 20 ++++++--- lib/provider/spotify/playlist/playlist.dart | 15 +------ lib/provider/spotify/utils/provider.dart | 45 +++++++++++++++++++ 4 files changed, 79 insertions(+), 45 deletions(-) diff --git a/lib/components/shared/dialogs/playlist_add_track_dialog.dart b/lib/components/shared/dialogs/playlist_add_track_dialog.dart index 60e43ca2..1f1807da 100644 --- a/lib/components/shared/dialogs/playlist_add_track_dialog.dart +++ b/lib/components/shared/dialogs/playlist_add_track_dialog.dart @@ -1,4 +1,3 @@ -import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; @@ -8,8 +7,7 @@ import 'package:spotify/spotify.dart'; import 'package:spotube/components/playlist/playlist_create_dialog.dart'; import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/extensions/context.dart'; -import 'package:spotube/provider/spotify_provider.dart'; -import 'package:spotube/services/queries/queries.dart'; +import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; class PlaylistAddTrackDialog extends HookConsumerWidget { @@ -25,27 +23,34 @@ class PlaylistAddTrackDialog extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { final ThemeData(:textTheme) = Theme.of(context); - final spotify = ref.watch(spotifyProvider); - final userPlaylists = useQueries.playlist.ofMineAll(ref); + final userPlaylists = ref.watch(favoritePlaylistsProvider); + final favoritePlaylistsNotifier = + ref.watch(favoritePlaylistsProvider.notifier); - final me = useQueries.user.me(ref); + final me = ref.watch(meProvider); final filteredPlaylists = useMemoized( () => - userPlaylists.data - ?.where( + userPlaylists.asData?.value.items + .where( (playlist) => playlist.owner?.id != null && - playlist.owner!.id == me.data?.id && + playlist.owner!.id == me.asData?.value.id && playlist.id != openFromPlaylist, ) .toList() ?? [], - [userPlaylists.data, me.data?.id, openFromPlaylist], + [userPlaylists.asData?.value, me.asData?.value.id, openFromPlaylist], ); final playlistsCheck = useState({}); - final queryClient = useQueryClient(); + + useEffect(() { + if (userPlaylists.asData?.value != null) { + favoritePlaylistsNotifier.fetchAll(); + } + return null; + }, [userPlaylists.asData?.value]); Future onAdd() async { final selectedPlaylists = playlistsCheck.value.entries @@ -54,21 +59,12 @@ class PlaylistAddTrackDialog extends HookConsumerWidget { await Future.wait( selectedPlaylists.map( - (playlistId) => spotify.playlists.addTracks( - tracks - .map( - (track) => track.uri!, - ) - .toList(), - playlistId), + (playlistId) => favoritePlaylistsNotifier.addTracks( + playlistId, + tracks.map((e) => e.id!).toList(), + ), ), ).then((_) => Navigator.pop(context, true)); - - await queryClient.refreshQueries( - selectedPlaylists - .map((playlistId) => "playlist-tracks/$playlistId") - .toList(), - ); } return AlertDialog( diff --git a/lib/provider/spotify/playlist/favorite.dart b/lib/provider/spotify/playlist/favorite.dart index d8fbfb9c..afa764ac 100644 --- a/lib/provider/spotify/playlist/favorite.dart +++ b/lib/provider/spotify/playlist/favorite.dart @@ -72,6 +72,19 @@ class FavoritePlaylistsNotifier ref.invalidate(isFavoritePlaylistProvider(playlist.id!)); } + + Future addTracks(String playlistId, List trackIds) async { + if (state.value == null) return; + + final spotify = ref.read(spotifyProvider); + + await spotify.playlists.addTracks( + trackIds.map((id) => 'spotify:track:$id').toList(), + playlistId, + ); + + ref.invalidate(playlistTracksProvider(playlistId)); + } } final favoritePlaylistsProvider = @@ -79,13 +92,6 @@ final favoritePlaylistsProvider = () => FavoritePlaylistsNotifier(), ); -final allFavoritePlaylistsProvider = FutureProvider>( - (ref) async { - final spotify = ref.watch(spotifyProvider); - return (await spotify.playlists.me.all()).toList(); - }, -); - final isFavoritePlaylistProvider = FutureProvider.family( (ref, id) async { final spotify = ref.watch(spotifyProvider); diff --git a/lib/provider/spotify/playlist/playlist.dart b/lib/provider/spotify/playlist/playlist.dart index 96a8634c..6f62000f 100644 --- a/lib/provider/spotify/playlist/playlist.dart +++ b/lib/provider/spotify/playlist/playlist.dart @@ -16,7 +16,7 @@ class PlaylistNotifier extends FamilyAsyncNotifier { } Future create(PlaylistInput input, [ValueChanged? onError]) async { - if (state is AsyncData || state is AsyncLoading) return; + if (state is AsyncLoading) return; state = const AsyncLoading(); final spotify = ref.read(spotifyProvider); @@ -51,19 +51,6 @@ class PlaylistNotifier extends FamilyAsyncNotifier { ref.invalidate(favoritePlaylistsProvider); } - Future addTracks(List trackIds) async { - if (state.value == null) return; - - final spotify = ref.read(spotifyProvider); - - await spotify.playlists.addTracks( - trackIds.map((id) => 'spotify:track:$id').toList(), - state.value!.id!, - ); - - ref.invalidate(playlistTracksProvider(state.value!.id!)); - } - Future modify(PlaylistInput input, [ValueChanged? onError]) async { if (state.value == null) return; diff --git a/lib/provider/spotify/utils/provider.dart b/lib/provider/spotify/utils/provider.dart index c952f9e8..bd8001aa 100644 --- a/lib/provider/spotify/utils/provider.dart +++ b/lib/provider/spotify/utils/provider.dart @@ -23,6 +23,30 @@ abstract class PaginatedAsyncNotifier> }, ); } + + Future> fetchAll() async { + if (state.value == null) return []; + if (!state.value!.hasMore) return state.value!.items; + + bool hasMore = true; + while (hasMore) { + await update((state) async { + final items = await fetch( + state.offset + state.limit, + state.limit, + ); + + hasMore = items.length == state.limit; + return state.copyWith( + items: [...state.items, ...items], + offset: state.offset + state.limit, + hasMore: hasMore, + ) as T; + }); + } + + return state.value!.items; + } } abstract class CursorPaginatedAsyncNotifier> fetchAll() async { + if (state.value == null) return []; + if (!state.value!.hasMore) return state.value!.items; + + bool hasMore = true; + while (hasMore) { + await update((state) async { + final items = await fetch(state.offset, state.limit); + + hasMore = items.$1.length == state.limit; + return state.copyWith( + items: [...state.items, ...items.$1], + offset: items.$2, + hasMore: hasMore, + ) as T; + }); + } + + return state.value!.items; + } } abstract class FamilyPaginatedAsyncNotifier<