From 25badae7ad94c45931be13d58bca9ac1cacee72d Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sun, 17 Mar 2024 12:33:11 +0600 Subject: [PATCH] feat: use provider for playlist and album card and heart button --- lib/components/album/album_card.dart | 27 +-- lib/components/player/player_actions.dart | 1 - lib/components/playlist/playlist_card.dart | 32 +--- lib/components/shared/heart_button.dart | 176 ++---------------- .../shared/track_tile/track_options.dart | 8 +- lib/provider/spotify/playlist/liked.dart | 2 +- 6 files changed, 33 insertions(+), 213 deletions(-) diff --git a/lib/components/album/album_card.dart b/lib/components/album/album_card.dart index 4d2e12d6..3838b7a4 100644 --- a/lib/components/album/album_card.dart +++ b/lib/components/album/album_card.dart @@ -1,15 +1,12 @@ -import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/shared/playbutton_card.dart'; import 'package:spotube/extensions/context.dart'; -import 'package:spotube/extensions/infinite_query.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; -import 'package:spotube/provider/spotify_provider.dart'; +import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; -import 'package:spotube/services/queries/album.dart'; import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; @@ -31,15 +28,12 @@ class AlbumCard extends HookConsumerWidget { useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying; final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier); - final queryClient = useQueryClient(); - bool isPlaylistPlaying = useMemoized( () => playlist.containsCollection(album.id!), [playlist, album.id], ); final updating = useState(false); - final spotify = ref.watch(spotifyProvider); final scaffoldMessenger = ScaffoldMessenger.maybeOf(context); @@ -50,23 +44,8 @@ class AlbumCard extends HookConsumerWidget { TypeConversionUtils.simpleTrack_X_Track(track, album)) .toList(); } - final job = AlbumQueries.tracksOfJob(album.id!); - - final query = queryClient.createInfiniteQuery( - job.queryKey, - (page) => job.task(page, (spotify: spotify, album: album)), - initialPage: 0, - nextPage: job.nextPage, - ); - - return await query.fetchAllTracks( - getAllTracks: () async { - final res = await spotify.albums.tracks(album.id!).all(); - return res - .map((e) => TypeConversionUtils.simpleTrack_X_Track(e, album)) - .toList(); - }, - ); + await ref.read(albumTracksProvider(album).future); + return ref.read(albumTracksProvider(album).notifier).fetchAll(); } return PlaybuttonCard( diff --git a/lib/components/player/player_actions.dart b/lib/components/player/player_actions.dart index 79dca1b0..18168af1 100644 --- a/lib/components/player/player_actions.dart +++ b/lib/components/player/player_actions.dart @@ -34,7 +34,6 @@ class PlayerActions extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { - final mediaQuery = MediaQuery.of(context); final playlist = ref.watch(ProxyPlaylistNotifier.provider); final isLocalTrack = playlist.activeTrack is LocalTrack; ref.watch(downloadManagerProvider); diff --git a/lib/components/playlist/playlist_card.dart b/lib/components/playlist/playlist_card.dart index 621a4f99..8713125b 100644 --- a/lib/components/playlist/playlist_card.dart +++ b/lib/components/playlist/playlist_card.dart @@ -1,14 +1,11 @@ -import 'package:fl_query/fl_query.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/shared/playbutton_card.dart'; -import 'package:spotube/extensions/infinite_query.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; -import 'package:spotube/provider/spotify_provider.dart'; +import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; -import 'package:spotube/services/queries/queries.dart'; import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; @@ -24,7 +21,6 @@ class PlaylistCard extends HookConsumerWidget { final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier); final playing = useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying; - final queryClient = QueryClient.of(context); final tracks = useState?>(null); bool isPlaylistPlaying = useMemoized( () => playlistQueue.containsCollection(playlist.id!), @@ -32,32 +28,16 @@ class PlaylistCard extends HookConsumerWidget { ); final updating = useState(false); - final spotify = ref.watch(spotifyProvider); - final me = useQueries.user.me(ref); + final me = ref.watch(meProvider); Future> fetchAllTracks() async { if (playlist.id == 'user-liked-tracks') { - return await queryClient.fetchQuery( - "user-liked-tracks", - () => useQueries.playlist.likedTracks(spotify), - ) ?? - []; + return await ref.read(likedTracksProvider.future); } - final query = queryClient.createInfiniteQuery, dynamic, int>( - "playlist-tracks/${playlist.id}", - (page) => useQueries.playlist.tracksOf(page, spotify, playlist.id!), - initialPage: 0, - nextPage: useQueries.playlist.tracksOfQueryNextPage, - ); + await ref.read(playlistTracksProvider(playlist.id!).future); - return await query.fetchAllTracks( - getAllTracks: () async { - final res = - await spotify.playlists.getTracksByPlaylistId(playlist.id!).all(); - return res.toList(); - }, - ); + return ref.read(playlistTracksProvider(playlist.id!).notifier).fetchAll(); } return PlaybuttonCard( @@ -71,7 +51,7 @@ class PlaylistCard extends HookConsumerWidget { isPlaying: isPlaylistPlaying, isLoading: (isPlaylistPlaying && playlistQueue.isFetching) || updating.value, - isOwner: playlist.owner?.id == me.data?.id && me.data?.id != null, + isOwner: playlist.owner?.id == me.value?.id && me.value?.id != null, onTap: () { ServiceUtils.push( context, diff --git a/lib/components/shared/heart_button.dart b/lib/components/shared/heart_button.dart index 1fabfb3a..27e11a41 100644 --- a/lib/components/shared/heart_button.dart +++ b/lib/components/shared/heart_button.dart @@ -1,5 +1,3 @@ -import 'package:fl_query/fl_query.dart'; -import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -8,8 +6,7 @@ import 'package:spotify/spotify.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/scrobbler_provider.dart'; -import 'package:spotube/services/mutations/mutations.dart'; -import 'package:spotube/services/queries/queries.dart'; +import 'package:spotube/provider/spotify/spotify.dart'; class HeartButton extends HookConsumerWidget { final bool isLiked; @@ -60,75 +57,32 @@ class HeartButton extends HookConsumerWidget { typedef UseTrackToggleLike = ({ bool isLiked, - Mutation toggleTrackLike, - Query me, + Future Function(Track track) toggleTrackLike, }); UseTrackToggleLike useTrackToggleLike(Track track, WidgetRef ref) { - final me = useQueries.user.me(ref); - - final savedTracks = useQueries.playlist.likedTracksQuery(ref); + final savedTracks = ref.watch(likedTracksProvider); + final savedTracksNotifier = ref.watch(likedTracksProvider.notifier); final isLiked = useMemoized( - () => savedTracks.data?.any((element) => element.id == track.id) ?? false, - [savedTracks.data, track.id], + () => savedTracks.value?.any((element) => element.id == track.id) ?? false, + [savedTracks.value, track.id], ); - final mounted = useIsMounted(); - final scrobblerNotifier = ref.read(scrobblerProvider.notifier); - final toggleTrackLike = useMutations.track.toggleFavorite( - ref, - track.id!, - onMutate: (isLiked) { - if (isLiked) { - savedTracks.setData( - savedTracks.data - ?.where((element) => element.id != track.id) - .toList() ?? - [], - ); - } else { - savedTracks.setData( - [ - ...?savedTracks.data, - track, - ], - ); - } - return isLiked; - }, - onData: (isLiked, recoveryData) async { - await savedTracks.refresh(); - if (isLiked) { + return ( + isLiked: isLiked, + toggleTrackLike: (track) async { + await savedTracksNotifier.toggleFavorite(track); + + if (!isLiked) { await scrobblerNotifier.love(track); } else { await scrobblerNotifier.unlove(track); } }, - onError: (payload, isLiked) { - if (!mounted()) return; - - if (isLiked != true) { - savedTracks.setData( - savedTracks.data - ?.where((element) => element.id != track.id) - .toList() ?? - [], - ); - } else { - savedTracks.setData( - [ - ...?savedTracks.data, - track, - ], - ); - } - }, ); - - return (isLiked: isLiked, toggleTrackLike: toggleTrackLike, me: me); } class TrackHeartButton extends HookConsumerWidget { @@ -140,10 +94,11 @@ class TrackHeartButton extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { - final savedTracks = useQueries.playlist.likedTracksQuery(ref); - final (:me, :isLiked, :toggleTrackLike) = useTrackToggleLike(track, ref); + final savedTracks = ref.watch(likedTracksProvider); + final me = ref.watch(meProvider); + final (:isLiked, :toggleTrackLike) = useTrackToggleLike(track, ref); - if (me.isLoading || !me.hasData) { + if (me.isLoading) { return const CircularProgressIndicator(); } @@ -152,104 +107,9 @@ class TrackHeartButton extends HookConsumerWidget { ? context.l10n.remove_from_favorites : context.l10n.save_as_favorite, isLiked: isLiked, - onPressed: savedTracks.hasData + onPressed: savedTracks.value != null ? () { - toggleTrackLike.mutate(isLiked); - } - : null, - ); - } -} - -class PlaylistHeartButton extends HookConsumerWidget { - final PlaylistSimple playlist; - final IconData? icon; - final ValueChanged? onData; - - const PlaylistHeartButton({ - required this.playlist, - super.key, - this.icon, - this.onData, - }); - - @override - Widget build(BuildContext context, ref) { - final me = useQueries.user.me(ref); - - final isLikedQuery = useQueries.playlist.doesUserFollow( - ref, - playlist.id!, - me.data?.id ?? '', - ); - - final togglePlaylistLike = useMutations.playlist.toggleFavorite( - ref, - playlist.id!, - refreshQueries: [ - isLikedQuery.key, - ], - onData: onData, - ); - - if (me.isLoading || !me.hasData) { - return const CircularProgressIndicator(); - } - - return HeartButton( - isLiked: isLikedQuery.data ?? false, - tooltip: isLikedQuery.data ?? false - ? context.l10n.remove_from_favorites - : context.l10n.save_as_favorite, - color: Colors.white, - icon: icon, - onPressed: isLikedQuery.hasData - ? () { - togglePlaylistLike.mutate(isLikedQuery.data!); - } - : null, - ); - } -} - -class AlbumHeartButton extends HookConsumerWidget { - final AlbumSimple album; - - const AlbumHeartButton({ - required this.album, - super.key, - }); - - @override - Widget build(BuildContext context, ref) { - final client = useQueryClient(); - final me = useQueries.user.me(ref); - - final albumIsSaved = useQueries.album.isSavedForMe(ref, album.id!); - final isLiked = albumIsSaved.data ?? false; - - final toggleAlbumLike = useMutations.album.toggleFavorite( - ref, - album.id!, - refreshQueries: [albumIsSaved.key], - onData: (_, __) async { - await client.refreshInfiniteQueryAllPages("current-user-albums"); - }, - ); - - if (me.isLoading || !me.hasData) { - return const CircularProgressIndicator(); - } - - return HeartButton( - isLiked: isLiked, - tooltip: isLiked - ? context.l10n.remove_from_favorites - : context.l10n.save_as_favorite, - color: Colors.white, - onPressed: albumIsSaved.hasData - ? () { - toggleAlbumLike.mutate(isLiked); + toggleTrackLike(track); } : null, ); diff --git a/lib/components/shared/track_tile/track_options.dart b/lib/components/shared/track_tile/track_options.dart index afa7a0b6..b2d0e5cd 100644 --- a/lib/components/shared/track_tile/track_options.dart +++ b/lib/components/shared/track_tile/track_options.dart @@ -23,6 +23,7 @@ import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/blacklist_provider.dart'; import 'package:spotube/provider/download_manager_provider.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; +import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/provider/spotify_provider.dart'; import 'package:spotube/services/mutations/mutations.dart'; import 'package:spotube/services/queries/search.dart'; @@ -176,6 +177,7 @@ class TrackOptions extends HookConsumerWidget { ref.watch(downloadManagerProvider); final downloadManager = ref.watch(downloadManagerProvider.notifier); final blacklist = ref.watch(BlackListNotifier.provider); + final me = ref.watch(meProvider); final favorites = useTrackToggleLike(track, ref); @@ -220,7 +222,7 @@ class TrackOptions extends HookConsumerWidget { break; case TrackOptionValue.delete: await File((track as LocalTrack).path).delete(); - ref.refresh(localTracksProvider); + ref.invalidate(localTracksProvider); break; case TrackOptionValue.addToQueue: await playback.addTrack(track); @@ -257,7 +259,7 @@ class TrackOptions extends HookConsumerWidget { ); break; case TrackOptionValue.favorite: - favorites.toggleTrackLike.mutate(favorites.isLiked); + favorites.toggleTrackLike(track); break; case TrackOptionValue.addToPlaylist: actionAddToPlaylist(context, track); @@ -361,7 +363,7 @@ class TrackOptions extends HookConsumerWidget { leading: const Icon(SpotubeIcons.queueRemove), title: Text(context.l10n.remove_from_queue), ), - if (favorites.me.hasData) + if (me.value != null) PopSheetEntry( value: TrackOptionValue.favorite, leading: favorites.isLiked diff --git a/lib/provider/spotify/playlist/liked.dart b/lib/provider/spotify/playlist/liked.dart index b8f6e79a..52463d3d 100644 --- a/lib/provider/spotify/playlist/liked.dart +++ b/lib/provider/spotify/playlist/liked.dart @@ -25,7 +25,7 @@ class LikedTracksNotifier extends AsyncNotifier> with Persistence { return tracks.where((e) => e.id != track.id).toList(); } else { await spotify.tracks.me.saveOne(track.id!); - return [...tracks, track]; + return [track, ...tracks]; } }); }