import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/provider/metadata_plugin/core/auth.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/tracks/playlist.dart'; import 'package:spotube/provider/metadata_plugin/utils/paginated.dart'; import 'package:spotube/services/metadata/errors/exceptions.dart'; class MetadataPluginSavedPlaylistsNotifier extends PaginatedAsyncNotifier { MetadataPluginSavedPlaylistsNotifier() : super(); @override fetch(int offset, int limit) async { final playlists = await (await metadataPlugin) .user .savedPlaylists(limit: limit, offset: offset); return playlists; } @override build() async { await ref.watch(metadataPluginAuthenticatedProvider.future); final playlists = await fetch(0, 20); return playlists; } void updatePlaylist(SpotubeSimplePlaylistObject playlist) { if (state.value == null) return; if (state.value!.items.none((e) => e.id == playlist.id)) return; state = AsyncData( state.value!.copyWith( items: state.value!.items .map((element) => element.id == playlist.id ? playlist : element) .toList(), ), ); } Future addFavorite(SpotubeSimplePlaylistObject playlist) async { if (state.value == null) return; final oldState = state.value; state = AsyncData( state.value!.copyWith( items: [ playlist, ...state.value!.items, ], ), ); try { await (await metadataPlugin).playlist.save(playlist.id); } catch (e) { state = AsyncData(oldState!); rethrow; } } Future removeFavorite(SpotubeSimplePlaylistObject playlist) async { if (state.value == null) return; final oldState = state.value; state = AsyncData( state.value!.copyWith( items: state.value!.items.where((e) => (e).id != playlist.id).toList(), ), ); try { await (await metadataPlugin).playlist.unsave(playlist.id); } catch (e) { state = AsyncData(oldState!); rethrow; } } Future delete(String playlistId) async { if (state.value == null) return; final oldState = state; try { state = const AsyncLoading(); await (await metadataPlugin).playlist.deletePlaylist(playlistId); ref.invalidateSelf(); ref.invalidate(metadataPluginIsSavedPlaylistProvider(playlistId)); ref.invalidate(metadataPluginPlaylistTracksProvider(playlistId)); } catch (e) { state = oldState; rethrow; } } Future addTracks(String playlistId, List trackIds) async { if (state.value == null) return; await (await metadataPlugin) .playlist .addTracks(playlistId, trackIds: trackIds); ref.invalidate(metadataPluginPlaylistTracksProvider(playlistId)); } Future removeTracks(String playlistId, List trackIds) async { if (state.value == null) return; await (await metadataPlugin) .playlist .removeTracks(playlistId, trackIds: trackIds); ref.invalidate(metadataPluginPlaylistTracksProvider(playlistId)); } } final metadataPluginSavedPlaylistsProvider = AsyncNotifierProvider< MetadataPluginSavedPlaylistsNotifier, SpotubePaginationResponseObject>( () => MetadataPluginSavedPlaylistsNotifier(), ); final metadataPluginIsSavedPlaylistProvider = FutureProvider.family( (ref, id) async { final plugin = await ref.watch(metadataPluginProvider.future); if (plugin == null) { throw MetadataPluginException.noDefaultPlugin(); } final savedPlaylists = await ref.watch(metadataPluginSavedPlaylistsProvider.future); final savedPlaylistsNotifier = ref.read(metadataPluginSavedPlaylistsProvider.notifier); final allSavedPlaylists = savedPlaylists.hasMore ? await savedPlaylistsNotifier.fetchAll() : savedPlaylists.items; return allSavedPlaylists.any((element) => element.id == id); }, );