diff --git a/lib/provider/spotify/album/favorite.dart b/lib/provider/spotify/album/favorite.dart index 8cdda862..e93176e2 100644 --- a/lib/provider/spotify/album/favorite.dart +++ b/lib/provider/spotify/album/favorite.dart @@ -41,12 +41,11 @@ class FavoriteAlbumNotifier ); } - Future saveAlbums(List ids) async { + Future addFavorites(List ids) async { if (state.value == null) return; - await spotify.me.saveAlbums(ids); - state = await AsyncValue.guard(() async { + await spotify.me.saveAlbums(ids); final albums = await spotify.albums.list(ids); return state.value!.copyWith( @@ -57,6 +56,20 @@ class FavoriteAlbumNotifier ); }); } + + Future removeFavorites(List ids) async { + if (state.value == null) return; + + state = await AsyncValue.guard(() async { + await spotify.me.removeAlbums(ids); + + return state.value!.copyWith( + items: state.value!.items + .where((element) => !ids.contains(element.id)) + .toList(), + ); + }); + } } final favoriteAlbumsProvider = diff --git a/lib/provider/spotify/playlist/favorite.dart b/lib/provider/spotify/playlist/favorite.dart index 0bf21741..9296bc51 100644 --- a/lib/provider/spotify/playlist/favorite.dart +++ b/lib/provider/spotify/playlist/favorite.dart @@ -50,6 +50,24 @@ class FavoritePlaylistsNotifier hasMore: playlists.length == 20, ); } + + Future addFavorite(PlaylistSimple playlist) async { + update((state) async { + await spotify.playlists.followPlaylist(playlist.id!); + return state.copyWith( + items: [...state.items, playlist], + ); + }); + } + + Future removeFavorite(PlaylistSimple playlist) async { + update((state) async { + await spotify.playlists.unfollowPlaylist(playlist.id!); + return state.copyWith( + items: state.items.where((e) => e.id != playlist.id).toList(), + ); + }); + } } final favoritePlaylistsProvider = diff --git a/lib/provider/spotify/playlist/liked.dart b/lib/provider/spotify/playlist/liked.dart index c48ff627..b8f6e79a 100644 --- a/lib/provider/spotify/playlist/liked.dart +++ b/lib/provider/spotify/playlist/liked.dart @@ -1,8 +1,49 @@ part of '../spotify.dart'; -final likedTracksProvider = FutureProvider>((ref) async { - final spotify = ref.watch(spotifyProvider); - final savedTracked = await spotify.tracks.me.saved.all(); +class LikedTracksNotifier extends AsyncNotifier> with Persistence { + LikedTracksNotifier() { + load(); + } - return savedTracked.map((e) => e.track!).toList(); -}); + @override + FutureOr> build() async { + final spotify = ref.watch(spotifyProvider); + final savedTracked = await spotify.tracks.me.saved.all(); + + return savedTracked.map((e) => e.track!).toList(); + } + + Future toggleFavorite(Track track) async { + if (state.value == null) return; + final spotify = ref.read(spotifyProvider); + + await update((tracks) async { + final isLiked = tracks.map((e) => e.id).contains(track.id); + + if (isLiked) { + await spotify.tracks.me.removeOne(track.id!); + return tracks.where((e) => e.id != track.id).toList(); + } else { + await spotify.tracks.me.saveOne(track.id!); + return [...tracks, track]; + } + }); + } + + @override + FutureOr> fromJson(Map json) { + return (json['tracks'] as List).map((e) => Track.fromJson(e)).toList(); + } + + @override + Map toJson(List data) { + return { + 'tracks': data.map((e) => e.toJson()).toList(), + }; + } +} + +final likedTracksProvider = + AsyncNotifierProvider>( + () => LikedTracksNotifier(), +); diff --git a/lib/provider/spotify/playlist/playlist.dart b/lib/provider/spotify/playlist/playlist.dart index d087a079..06ed50f8 100644 --- a/lib/provider/spotify/playlist/playlist.dart +++ b/lib/provider/spotify/playlist/playlist.dart @@ -1,8 +1,89 @@ part of '../spotify.dart'; -final playlistProvider = FutureProvider.family( - (ref, playlistId) async { +typedef PlaylistInput = ({ + String playlistName, + bool? public, + bool? collaborative, + String? description, + String? base64Image, +}); + +class PlaylistNotifier extends FamilyAsyncNotifier { + @override + FutureOr build(String arg) { final spotify = ref.watch(spotifyProvider); - return spotify.playlists.get(playlistId); - }, + return spotify.playlists.get(arg); + } + + Future create(PlaylistInput input) async { + if (state is AsyncData || state is AsyncLoading) return; + state = const AsyncLoading(); + + final spotify = ref.read(spotifyProvider); + final me = ref.read(meProvider); + + if (me.value == null) return; + + state = await AsyncValue.guard(() async { + final playlist = await spotify.playlists.createPlaylist( + me.value!.id!, + input.playlistName, + collaborative: input.collaborative, + description: input.description, + public: input.public, + ); + + if (input.base64Image != null) { + await spotify.playlists.updatePlaylistImage( + playlist.id!, + input.base64Image!, + ); + } + + return playlist; + }); + } + + 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.refresh(playlistTracksProvider(state.value!.id!)); + } + + Future modify(PlaylistInput input) async { + if (state.value == null) return; + + final spotify = ref.read(spotifyProvider); + + await update((state) async { + await spotify.playlists.updatePlaylist( + state.id!, + input.playlistName, + collaborative: input.collaborative, + description: input.description, + public: input.public, + ); + + if (input.base64Image != null) { + await spotify.playlists.updatePlaylistImage( + state.id!, + input.base64Image!, + ); + } + + return spotify.playlists.get(state.id!); + }); + } +} + +final playlistProvider = + AsyncNotifierProvider.family( + () => PlaylistNotifier(), ); diff --git a/lib/provider/spotify/spotify.dart b/lib/provider/spotify/spotify.dart index 551a3c60..cb340576 100644 --- a/lib/provider/spotify/spotify.dart +++ b/lib/provider/spotify/spotify.dart @@ -11,6 +11,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; // ignore: depend_on_referenced_packages, implementation_imports import 'package:riverpod/src/async_notifier.dart'; import 'package:spotube/extensions/map.dart'; +import 'package:spotube/extensions/track.dart'; import 'package:spotube/models/lyrics.dart'; import 'package:spotube/models/spotify/recommendation_seeds.dart'; import 'package:spotube/models/spotify_friends.dart';