feat: use provider in add track dialog

This commit is contained in:
Kingkor Roy Tirtho 2024-03-17 15:21:57 +06:00
parent 82802fef2c
commit d2a9ff6652
4 changed files with 79 additions and 45 deletions

View File

@ -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(<String, bool>{});
final queryClient = useQueryClient();
useEffect(() {
if (userPlaylists.asData?.value != null) {
favoritePlaylistsNotifier.fetchAll();
}
return null;
}, [userPlaylists.asData?.value]);
Future<void> 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(

View File

@ -72,6 +72,19 @@ class FavoritePlaylistsNotifier
ref.invalidate(isFavoritePlaylistProvider(playlist.id!));
}
Future<void> addTracks(String playlistId, List<String> 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<List<PlaylistSimple>>(
(ref) async {
final spotify = ref.watch(spotifyProvider);
return (await spotify.playlists.me.all()).toList();
},
);
final isFavoritePlaylistProvider = FutureProvider.family<bool, String>(
(ref, id) async {
final spotify = ref.watch(spotifyProvider);

View File

@ -16,7 +16,7 @@ class PlaylistNotifier extends FamilyAsyncNotifier<Playlist, String> {
}
Future<void> 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<Playlist, String> {
ref.invalidate(favoritePlaylistsProvider);
}
Future<void> addTracks(List<String> 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<void> modify(PlaylistInput input, [ValueChanged? onError]) async {
if (state.value == null) return;

View File

@ -23,6 +23,30 @@ abstract class PaginatedAsyncNotifier<K, T extends BasePaginatedState<K, int>>
},
);
}
Future<List<K>> 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<K,
@ -49,6 +73,27 @@ abstract class CursorPaginatedAsyncNotifier<K,
},
);
}
Future<List<K>> 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<