feat: use providers on library page

This commit is contained in:
Kingkor Roy Tirtho 2024-03-17 12:03:20 +06:00
parent 786342912b
commit 4ae530d29d
6 changed files with 51 additions and 53 deletions

View File

@ -4,7 +4,6 @@ import 'package:collection/collection.dart';
import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart'; import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/fake.dart'; import 'package:spotube/collections/fake.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
@ -15,7 +14,7 @@ import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
import 'package:spotube/components/shared/waypoint.dart'; import 'package:spotube/components/shared/waypoint.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/services/queries/queries.dart'; import 'package:spotube/provider/spotify/spotify.dart';
import 'package:spotube/utils/type_conversion_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart';
@ -25,32 +24,28 @@ class UserAlbums extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final auth = ref.watch(AuthenticationNotifier.provider); final auth = ref.watch(AuthenticationNotifier.provider);
final albumsQuery = useQueries.album.ofMine(ref); final albumsQuery = ref.watch(favoriteAlbumsProvider);
final albumsQueryNotifier = ref.watch(favoriteAlbumsProvider.notifier);
final controller = useScrollController(); final controller = useScrollController();
final searchText = useState(''); final searchText = useState('');
final allAlbums = useMemoized(
() => albumsQuery.pages
.expand((element) => element.items ?? <AlbumSimple>[]),
[albumsQuery.pages],
);
final albums = useMemoized(() { final albums = useMemoized(() {
if (searchText.value.isEmpty) { if (searchText.value.isEmpty) {
return allAlbums; return albumsQuery.value?.items ?? [];
} }
return allAlbums return albumsQuery.value?.items
.map((e) => ( .map((e) => (
weightedRatio(e.name!, searchText.value), weightedRatio(e.name!, searchText.value),
e, e,
)) ))
.sorted((a, b) => b.$1.compareTo(a.$1)) .sorted((a, b) => b.$1.compareTo(a.$1))
.where((e) => e.$1 > 50) .where((e) => e.$1 > 50)
.map((e) => e.$2) .map((e) => e.$2)
.toList(); .toList() ??
}, [allAlbums, searchText.value]); [];
}, [albumsQuery.value, searchText.value]);
if (auth == null) { if (auth == null) {
return const AnonymousFallback(); return const AnonymousFallback();
@ -60,7 +55,7 @@ class UserAlbums extends HookConsumerWidget {
return RefreshIndicator( return RefreshIndicator(
onRefresh: () async { onRefresh: () async {
await albumsQuery.refresh(); ref.invalidate(favoriteAlbumsProvider);
}, },
child: SafeArea( child: SafeArea(
child: Scaffold( child: Scaffold(
@ -85,7 +80,7 @@ class UserAlbums extends HookConsumerWidget {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
controller: controller, controller: controller,
child: Skeletonizer( child: Skeletonizer(
enabled: albumsQuery.pages.isEmpty, enabled: albumsQuery.isLoadingAndEmpty,
child: Center( child: Center(
child: Wrap( child: Wrap(
runSpacing: 20, runSpacing: 20,
@ -93,7 +88,8 @@ class UserAlbums extends HookConsumerWidget {
runAlignment: WrapAlignment.center, runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
if (albumsQuery.pages.isEmpty) if (albumsQuery.value == null ||
albumsQuery.value!.items.isEmpty)
...List.generate( ...List.generate(
10, 10,
(index) => AlbumCard(FakeData.album), (index) => AlbumCard(FakeData.album),
@ -107,11 +103,12 @@ class UserAlbums extends HookConsumerWidget {
AlbumCard( AlbumCard(
TypeConversionUtils.simpleAlbum_X_Album(album), TypeConversionUtils.simpleAlbum_X_Album(album),
), ),
if (albums.isNotEmpty && albumsQuery.hasNextPage) if (albums.isNotEmpty &&
albumsQuery.value?.hasMore == true)
Waypoint( Waypoint(
controller: controller, controller: controller,
isGrid: true, isGrid: true,
onTouchEdge: albumsQuery.fetchNext, onTouchEdge: albumsQueryNotifier.fetchMore,
child: AlbumCard(FakeData.album), child: AlbumCard(FakeData.album),
) )
], ],

View File

@ -13,7 +13,7 @@ import 'package:spotube/components/shared/fallbacks/not_found.dart';
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart'; import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/services/queries/queries.dart'; import 'package:spotube/provider/spotify/spotify.dart';
class UserArtists extends HookConsumerWidget { class UserArtists extends HookConsumerWidget {
const UserArtists({super.key}); const UserArtists({super.key});
@ -23,12 +23,12 @@ class UserArtists extends HookConsumerWidget {
final theme = Theme.of(context); final theme = Theme.of(context);
final auth = ref.watch(AuthenticationNotifier.provider); final auth = ref.watch(AuthenticationNotifier.provider);
final artistQuery = useQueries.artist.followedByMeAll(ref); final artistQuery = ref.watch(followedArtistsProvider);
final searchText = useState(''); final searchText = useState('');
final filteredArtists = useMemoized(() { final filteredArtists = useMemoized(() {
final artists = artistQuery.data ?? []; final artists = artistQuery.value?.items ?? [];
if (searchText.value.isEmpty) { if (searchText.value.isEmpty) {
return artists.toList(); return artists.toList();
@ -42,7 +42,7 @@ class UserArtists extends HookConsumerWidget {
.where((e) => e.$1 > 50) .where((e) => e.$1 > 50)
.map((e) => e.$2) .map((e) => e.$2)
.toList(); .toList();
}, [artistQuery.data, searchText.value]); }, [artistQuery.value?.items, searchText.value]);
final controller = useScrollController(); final controller = useScrollController();
@ -66,7 +66,7 @@ class UserArtists extends HookConsumerWidget {
), ),
), ),
backgroundColor: theme.scaffoldBackgroundColor, backgroundColor: theme.scaffoldBackgroundColor,
body: artistQuery.data?.isEmpty == true body: artistQuery.value?.items.isEmpty == true
? Padding( ? Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Row( child: Row(
@ -80,7 +80,7 @@ class UserArtists extends HookConsumerWidget {
) )
: RefreshIndicator( : RefreshIndicator(
onRefresh: () async { onRefresh: () async {
await artistQuery.refresh(); ref.invalidate(followedArtistsProvider);
}, },
child: InterScrollbar( child: InterScrollbar(
controller: controller, controller: controller,
@ -109,8 +109,9 @@ class UserArtists extends HookConsumerWidget {
) )
] ]
: filteredArtists : filteredArtists
.mapIndexed((index, artist) => .mapIndexed(
ArtistCard(artist)) (index, artist) => ArtistCard(artist),
)
.toList(), .toList(),
), ),
), ),

View File

@ -217,7 +217,7 @@ class UserLocalTracks extends HookConsumerWidget {
FilledButton( FilledButton(
child: const Icon(SpotubeIcons.refresh), child: const Icon(SpotubeIcons.refresh),
onPressed: () { onPressed: () {
ref.refresh(localTracksProvider); ref.invalidate(localTracksProvider);
}, },
) )
], ],
@ -269,7 +269,7 @@ class UserLocalTracks extends HookConsumerWidget {
return Expanded( return Expanded(
child: RefreshIndicator( child: RefreshIndicator(
onRefresh: () async { onRefresh: () async {
ref.refresh(localTracksProvider); ref.invalidate(localTracksProvider);
}, },
child: InterScrollbar( child: InterScrollbar(
controller: controller, controller: controller,

View File

@ -17,7 +17,7 @@ import 'package:spotube/components/shared/waypoint.dart';
import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/services/queries/queries.dart'; import 'package:spotube/provider/spotify/spotify.dart';
class UserPlaylists extends HookConsumerWidget { class UserPlaylists extends HookConsumerWidget {
const UserPlaylists({super.key}); const UserPlaylists({super.key});
@ -28,13 +28,9 @@ class UserPlaylists extends HookConsumerWidget {
final auth = ref.watch(AuthenticationNotifier.provider); final auth = ref.watch(AuthenticationNotifier.provider);
final playlistsQuery = useQueries.playlist.ofMine(ref); final playlistsQuery = ref.watch(favoritePlaylistsProvider);
final playlistsQueryNotifier =
final pagePlaylists = useMemoized( ref.watch(favoritePlaylistsProvider.notifier);
() => playlistsQuery.pages
.expand((page) => page.items?.toList() ?? <PlaylistSimple>[]),
[playlistsQuery.pages],
);
final likedTracksPlaylist = useMemoized( final likedTracksPlaylist = useMemoized(
() => PlaylistSimple() () => PlaylistSimple()
@ -58,12 +54,12 @@ class UserPlaylists extends HookConsumerWidget {
if (searchText.value.isEmpty) { if (searchText.value.isEmpty) {
return [ return [
likedTracksPlaylist, likedTracksPlaylist,
...pagePlaylists, ...?playlistsQuery.value?.items,
]; ];
} }
return [ return [
likedTracksPlaylist, likedTracksPlaylist,
...pagePlaylists, ...?playlistsQuery.value?.items,
] ]
.map((e) => (weightedRatio(e.name!, searchText.value), e)) .map((e) => (weightedRatio(e.name!, searchText.value), e))
.sorted((a, b) => b.$1.compareTo(a.$1)) .sorted((a, b) => b.$1.compareTo(a.$1))
@ -71,7 +67,7 @@ class UserPlaylists extends HookConsumerWidget {
.map((e) => e.$2) .map((e) => e.$2)
.toList(); .toList();
}, },
[pagePlaylists, searchText.value], [playlistsQuery, searchText.value],
); );
final controller = useScrollController(); final controller = useScrollController();
@ -81,7 +77,9 @@ class UserPlaylists extends HookConsumerWidget {
} }
return RefreshIndicator( return RefreshIndicator(
onRefresh: playlistsQuery.refresh, onRefresh: () async {
ref.invalidate(favoritePlaylistsProvider);
},
child: SafeArea( child: SafeArea(
child: InterScrollbar( child: InterScrollbar(
controller: controller, controller: controller,
@ -132,14 +130,14 @@ class UserPlaylists extends HookConsumerWidget {
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (playlists.isNotEmpty && index == playlists.length) { if (playlists.isNotEmpty && index == playlists.length) {
if (!playlistsQuery.hasNextPage) { if (playlistsQuery.value?.hasMore != true) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
return Waypoint( return Waypoint(
controller: controller, controller: controller,
isGrid: true, isGrid: true,
onTouchEdge: playlistsQuery.fetchNext, onTouchEdge: playlistsQueryNotifier.fetchMore,
child: Skeletonizer( child: Skeletonizer(
enabled: true, enabled: true,
child: PlaylistCard(FakeData.playlistSimple), child: PlaylistCard(FakeData.playlistSimple),

View File

@ -1,8 +1,10 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/spotify_provider.dart';
import 'package:spotube/services/custom_spotify_endpoints/spotify_endpoints.dart'; import 'package:spotube/services/custom_spotify_endpoints/spotify_endpoints.dart';
final customSpotifyEndpointProvider = Provider<CustomSpotifyEndpoints>((ref) { final customSpotifyEndpointProvider = Provider<CustomSpotifyEndpoints>((ref) {
ref.watch(spotifyProvider);
final auth = ref.watch(AuthenticationNotifier.provider); final auth = ref.watch(AuthenticationNotifier.provider);
return CustomSpotifyEndpoints(auth?.accessToken ?? ""); return CustomSpotifyEndpoints(auth?.accessToken ?? "");
}); });

View File

@ -1,6 +1,6 @@
part of '../spotify.dart'; part of '../spotify.dart';
class FollowedArtistsState extends CursorPaginatedState<ArtistSimple> { class FollowedArtistsState extends CursorPaginatedState<Artist> {
FollowedArtistsState({ FollowedArtistsState({
required super.items, required super.items,
required super.offset, required super.offset,
@ -10,7 +10,7 @@ class FollowedArtistsState extends CursorPaginatedState<ArtistSimple> {
@override @override
FollowedArtistsState copyWith({ FollowedArtistsState copyWith({
List<ArtistSimple>? items, List<Artist>? items,
String? offset, String? offset,
int? limit, int? limit,
bool? hasMore, bool? hasMore,
@ -25,7 +25,7 @@ class FollowedArtistsState extends CursorPaginatedState<ArtistSimple> {
} }
class FollowedArtistsNotifier class FollowedArtistsNotifier
extends CursorPaginatedAsyncNotifier<ArtistSimple, FollowedArtistsState> { extends CursorPaginatedAsyncNotifier<Artist, FollowedArtistsState> {
FollowedArtistsNotifier() : super(); FollowedArtistsNotifier() : super();
@override @override
@ -95,7 +95,7 @@ final followedArtistsProvider =
() => FollowedArtistsNotifier(), () => FollowedArtistsNotifier(),
); );
final allFollowedArtistsProvider = FutureProvider<List<ArtistSimple>>( final allFollowedArtistsProvider = FutureProvider<List<Artist>>(
(ref) async { (ref) async {
final spotify = ref.watch(spotifyProvider); final spotify = ref.watch(spotifyProvider);
final artists = await spotify.me.following(FollowingType.artist).all(); final artists = await spotify.me.following(FollowingType.artist).all();