mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-05-08 16:24:36 +00:00
feat: use providers in home screen
This commit is contained in:
parent
f83d6e08e1
commit
94b3e160c2
@ -1,35 +1,28 @@
|
||||
import 'package:flutter/material.dart' hide Page;
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
|
||||
class HomeFeaturedSection extends HookConsumerWidget {
|
||||
const HomeFeaturedSection({Key? key}) : super(key: key);
|
||||
const HomeFeaturedSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final featuredPlaylistsQuery = useQueries.playlist.featured(ref);
|
||||
final playlists = useMemoized(
|
||||
() => featuredPlaylistsQuery.pages
|
||||
.whereType<Page<PlaylistSimple>>()
|
||||
.expand((page) => page.items ?? const <PlaylistSimple>[]),
|
||||
[featuredPlaylistsQuery.pages],
|
||||
);
|
||||
final isLoadingFeaturedPlaylists = !featuredPlaylistsQuery.hasPageData &&
|
||||
!featuredPlaylistsQuery.isLoadingNextPage;
|
||||
final featuredPlaylists = ref.watch(featuredPlaylistsProvider);
|
||||
final featuredPlaylistsNotifier =
|
||||
ref.read(featuredPlaylistsProvider.notifier);
|
||||
|
||||
return Skeletonizer(
|
||||
enabled: isLoadingFeaturedPlaylists,
|
||||
enabled: featuredPlaylists.isLoadingAndEmpty,
|
||||
child: HorizontalPlaybuttonCardView<PlaylistSimple>(
|
||||
items: playlists.toList(),
|
||||
items: featuredPlaylists.value?.items ?? [],
|
||||
title: Text(context.l10n.featured),
|
||||
isLoadingNextPage: featuredPlaylistsQuery.isLoadingNextPage,
|
||||
hasNextPage: featuredPlaylistsQuery.hasNextPage,
|
||||
onFetchMore: featuredPlaylistsQuery.fetchNext,
|
||||
isLoadingNextPage: featuredPlaylists.isLoadingNextPage,
|
||||
hasNextPage: featuredPlaylists.value?.hasMore ?? false,
|
||||
onFetchMore: featuredPlaylistsNotifier.fetchMore,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import 'dart:ffi';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@ -8,15 +7,15 @@ import 'package:spotube/collections/fake.dart';
|
||||
import 'package:spotube/components/home/sections/friends/friend_item.dart';
|
||||
import 'package:spotube/hooks/utils/use_breakpoint_value.dart';
|
||||
import 'package:spotube/models/spotify_friends.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
|
||||
class HomePageFriendsSection extends HookConsumerWidget {
|
||||
const HomePageFriendsSection({Key? key}) : super(key: key);
|
||||
const HomePageFriendsSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final friendsQuery = useQueries.user.friendActivity(ref);
|
||||
final friends = friendsQuery.data?.friends ?? FakeData.friends.friends;
|
||||
final friendsQuery = ref.watch(friendsProvider);
|
||||
final friends = friendsQuery.value?.friends ?? FakeData.friends.friends;
|
||||
|
||||
final groupCount = useBreakpointValue(
|
||||
sm: 3,
|
||||
@ -51,8 +50,7 @@ class HomePageFriendsSection extends HookConsumerWidget {
|
||||
},
|
||||
);
|
||||
|
||||
if (!friendsQuery.isLoading &&
|
||||
(!friendsQuery.hasData || friendsQuery.data!.friends.isEmpty)) {
|
||||
if (friendsQuery.isLoading || friendsQuery.value?.friends.isEmpty == true) {
|
||||
return const SliverToBoxAdapter(
|
||||
child: SizedBox.shrink(),
|
||||
);
|
||||
|
||||
@ -13,9 +13,9 @@ import 'package:spotube/provider/spotify_provider.dart';
|
||||
class FriendItem extends HookConsumerWidget {
|
||||
final SpotifyFriendActivity friend;
|
||||
const FriendItem({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.friend,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
|
||||
@ -13,28 +13,26 @@ import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||
import 'package:spotube/extensions/constrains.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
|
||||
class HomeGenresSection extends HookConsumerWidget {
|
||||
const HomeGenresSection({Key? key}) : super(key: key);
|
||||
const HomeGenresSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final ThemeData(:textTheme, :colorScheme) = Theme.of(context);
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
|
||||
final recommendationMarket = ref.watch(
|
||||
userPreferencesProvider.select((s) => s.recommendationMarket),
|
||||
);
|
||||
final categoriesQuery =
|
||||
useQueries.category.listAll(ref, recommendationMarket);
|
||||
|
||||
final categories = categoriesQuery.data
|
||||
final categoriesQuery = ref.watch(categoriesProvider);
|
||||
final categories = useMemoized(
|
||||
() =>
|
||||
categoriesQuery.value
|
||||
?.where((c) => (c.icons?.length ?? 0) > 0)
|
||||
.take(mediaQuery.mdAndDown ? 6 : 10)
|
||||
.toList() ??
|
||||
<Category>[];
|
||||
<Category>[],
|
||||
[mediaQuery.mdAndDown, categoriesQuery.value],
|
||||
);
|
||||
|
||||
return SliverMainAxisGroup(
|
||||
slivers: [
|
||||
|
||||
@ -2,19 +2,19 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
|
||||
class HomeMadeForUserSection extends HookConsumerWidget {
|
||||
const HomeMadeForUserSection({Key? key}) : super(key: key);
|
||||
const HomeMadeForUserSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final madeForUser = useQueries.views.get(ref, "made-for-x-hub");
|
||||
final madeForUser = ref.watch(viewProvider("made-for-x-hub"));
|
||||
|
||||
return SliverList.builder(
|
||||
itemCount: madeForUser.data?["content"]?["items"]?.length ?? 0,
|
||||
itemCount: madeForUser.value?["content"]?["items"]?.length ?? 0,
|
||||
itemBuilder: (context, index) {
|
||||
final item = madeForUser.data?["content"]?["items"]?[index];
|
||||
final item = madeForUser.value?["content"]?["items"]?[index];
|
||||
final playlists = item["content"]?["items"]
|
||||
?.where((itemL2) => itemL2["type"] == "playlist")
|
||||
.map((itemL2) => PlaylistSimple.fromJson(itemL2))
|
||||
|
||||
@ -5,52 +5,32 @@ import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
|
||||
class HomeNewReleasesSection extends HookConsumerWidget {
|
||||
const HomeNewReleasesSection({Key? key}) : super(key: key);
|
||||
const HomeNewReleasesSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||
|
||||
final newReleases = useQueries.album.newReleases(ref);
|
||||
final userArtistsQuery = useQueries.artist.followedByMeAll(ref);
|
||||
final userArtists =
|
||||
userArtistsQuery.data?.map((s) => s.id!).toList() ?? const [];
|
||||
final newReleases = ref.watch(albumReleasesProvider);
|
||||
final newReleasesNotifier = ref.read(albumReleasesProvider.notifier);
|
||||
|
||||
final albums = useMemoized(
|
||||
() {
|
||||
final allReleases = newReleases.pages
|
||||
.whereType<Page<AlbumSimple>>()
|
||||
.expand((page) => page.items ?? const <AlbumSimple>[])
|
||||
.map((album) => TypeConversionUtils.simpleAlbum_X_Album(album));
|
||||
final albums = ref.watch(userArtistAlbumReleasesProvider);
|
||||
|
||||
final userArtistReleases = allReleases.where((album) {
|
||||
return album.artists
|
||||
?.any((artist) => userArtists.contains(artist.id!)) ==
|
||||
true;
|
||||
}).toList();
|
||||
|
||||
if (userArtistReleases.isEmpty) return allReleases.toList();
|
||||
return userArtistReleases;
|
||||
},
|
||||
[newReleases.pages],
|
||||
);
|
||||
|
||||
final hasNewReleases = newReleases.hasPageData &&
|
||||
userArtistsQuery.hasData &&
|
||||
!newReleases.isLoadingNextPage;
|
||||
|
||||
if (auth == null || !hasNewReleases) return const SizedBox.shrink();
|
||||
if (auth == null ||
|
||||
newReleases.isLoading ||
|
||||
newReleases.value?.items.isEmpty == true) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return HorizontalPlaybuttonCardView<Album>(
|
||||
items: albums,
|
||||
title: Text(context.l10n.new_releases),
|
||||
isLoadingNextPage: newReleases.isLoadingNextPage,
|
||||
hasNextPage: newReleases.hasNextPage,
|
||||
onFetchMore: newReleases.fetchNext,
|
||||
hasNextPage: newReleases.value?.hasMore ?? false,
|
||||
onFetchMore: newReleasesNotifier.fetchMore,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,13 +24,12 @@ class HorizontalPlaybuttonCardView<T> extends HookWidget {
|
||||
required this.hasNextPage,
|
||||
required this.onFetchMore,
|
||||
required this.isLoadingNextPage,
|
||||
Key? key,
|
||||
super.key,
|
||||
}) : assert(
|
||||
items is List<PlaylistSimple> ||
|
||||
items is List<Album> ||
|
||||
items is List<Artist>,
|
||||
),
|
||||
super(key: key);
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
@ -12,7 +10,7 @@ import 'package:spotube/components/shared/image/universal_image.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
import 'package:spotube/components/shared/waypoint.dart';
|
||||
import 'package:spotube/extensions/constrains.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||
|
||||
@ -22,23 +20,10 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final playlistsQuery = useQueries.category.playlistsOf(
|
||||
ref,
|
||||
category.id!,
|
||||
);
|
||||
|
||||
final playlists = useMemoized(
|
||||
() => playlistsQuery.pages.expand(
|
||||
(page) {
|
||||
return page.items?.whereNotNull() ??
|
||||
const Iterable<PlaylistSimple>.empty();
|
||||
},
|
||||
).toList(),
|
||||
[playlistsQuery.pages],
|
||||
);
|
||||
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
|
||||
final playlists = ref.watch(categoryPlaylistsProvider(category.id!));
|
||||
final playlistsNotifier =
|
||||
ref.read(categoryPlaylistsProvider(category.id!).notifier);
|
||||
final scrollController = useScrollController();
|
||||
|
||||
return Scaffold(
|
||||
@ -109,7 +94,7 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: mediaQuery.mdAndDown ? 12 : 24,
|
||||
),
|
||||
sliver: playlists.isEmpty
|
||||
sliver: playlists.value?.items.isNotEmpty != true
|
||||
? Skeletonizer.sliver(
|
||||
child: SliverToBoxAdapter(
|
||||
child: Wrap(
|
||||
@ -129,12 +114,13 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
itemCount: playlists.length + 1,
|
||||
itemCount: (playlists.value?.items.length ?? 0) + 1,
|
||||
itemBuilder: (context, index) {
|
||||
final playlist = playlists.elementAtOrNull(index);
|
||||
final playlist =
|
||||
playlists.value?.items.elementAtOrNull(index);
|
||||
|
||||
if (playlist == null) {
|
||||
if (!playlistsQuery.hasNextPage) {
|
||||
if (playlists.value?.hasMore == false) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Skeletonizer(
|
||||
@ -142,11 +128,7 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
child: Waypoint(
|
||||
controller: scrollController,
|
||||
isGrid: true,
|
||||
onTouchEdge: () async {
|
||||
if (playlistsQuery.hasNextPage) {
|
||||
await playlistsQuery.fetchNext();
|
||||
}
|
||||
},
|
||||
onTouchEdge: playlistsNotifier.fetchMore,
|
||||
child: PlaylistCard(FakeData.playlist),
|
||||
),
|
||||
);
|
||||
|
||||
@ -5,14 +5,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart' hide Offset;
|
||||
import 'package:spotube/collections/gradients.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
import 'package:spotube/extensions/constrains.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
|
||||
class GenrePage extends HookConsumerWidget {
|
||||
const GenrePage({super.key});
|
||||
@ -21,13 +18,7 @@ class GenrePage extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
final ThemeData(:textTheme) = Theme.of(context);
|
||||
final scrollController = useScrollController();
|
||||
final recommendationMarket = ref.watch(
|
||||
userPreferencesProvider.select((s) => s.recommendationMarket),
|
||||
);
|
||||
final categoriesQuery =
|
||||
useQueries.category.listAll(ref, recommendationMarket);
|
||||
|
||||
final categories = categoriesQuery.data ?? <Category>[];
|
||||
final categories = ref.watch(categoriesProvider);
|
||||
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
|
||||
@ -48,9 +39,9 @@ class GenrePage extends HookConsumerWidget {
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
itemCount: categories.length,
|
||||
itemCount: categories.value!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final category = categories[index];
|
||||
final category = categories.value![index];
|
||||
final gradient = gradients[Random().nextInt(gradients.length)];
|
||||
return InkWell(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
|
||||
@ -11,7 +11,7 @@ import 'package:spotube/components/home/sections/new_releases.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
|
||||
class HomePage extends HookConsumerWidget {
|
||||
const HomePage({Key? key}) : super(key: key);
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
part of '../spotify.dart';
|
||||
|
||||
class AlbumReleasesState extends PaginatedState<AlbumSimple> {
|
||||
class AlbumReleasesState extends PaginatedState<Album> {
|
||||
AlbumReleasesState({
|
||||
required super.items,
|
||||
required super.offset,
|
||||
@ -10,7 +10,7 @@ class AlbumReleasesState extends PaginatedState<AlbumSimple> {
|
||||
|
||||
@override
|
||||
AlbumReleasesState copyWith({
|
||||
List<AlbumSimple>? items,
|
||||
List<Album>? items,
|
||||
int? offset,
|
||||
int? limit,
|
||||
bool? hasMore,
|
||||
@ -25,16 +25,21 @@ class AlbumReleasesState extends PaginatedState<AlbumSimple> {
|
||||
}
|
||||
|
||||
class AlbumReleasesNotifier
|
||||
extends PaginatedAsyncNotifier<AlbumSimple, AlbumReleasesState> {
|
||||
extends PaginatedAsyncNotifier<Album, AlbumReleasesState> {
|
||||
AlbumReleasesNotifier() : super();
|
||||
|
||||
@override
|
||||
fetch(int offset, int limit) async {
|
||||
final market = ref.read(userPreferencesProvider).recommendationMarket;
|
||||
|
||||
final albums = await spotify.browse
|
||||
.newReleases(country: market)
|
||||
.getPage(offset, limit);
|
||||
return albums.items?.toList() ?? [];
|
||||
.getPage(limit, offset);
|
||||
|
||||
return albums.items
|
||||
?.map(TypeConversionUtils.simpleAlbum_X_Album)
|
||||
.toList() ??
|
||||
[];
|
||||
}
|
||||
|
||||
@override
|
||||
@ -43,7 +48,10 @@ class AlbumReleasesNotifier
|
||||
ref.watch(
|
||||
userPreferencesProvider.select((s) => s.recommendationMarket),
|
||||
);
|
||||
ref.watch(allFollowedArtistsProvider);
|
||||
|
||||
final albums = await fetch(0, 20);
|
||||
|
||||
return AlbumReleasesState(
|
||||
items: albums,
|
||||
offset: 0,
|
||||
@ -57,3 +65,26 @@ final albumReleasesProvider =
|
||||
AsyncNotifierProvider<AlbumReleasesNotifier, AlbumReleasesState>(
|
||||
() => AlbumReleasesNotifier(),
|
||||
);
|
||||
|
||||
final userArtistAlbumReleasesProvider = Provider<List<Album>>((ref) {
|
||||
final newReleases = ref.watch(albumReleasesProvider);
|
||||
final userArtistsQuery = ref.watch(allFollowedArtistsProvider);
|
||||
|
||||
if (newReleases.isLoading || userArtistsQuery.isLoading) {
|
||||
return const [];
|
||||
}
|
||||
|
||||
final userArtists =
|
||||
userArtistsQuery.value?.map((s) => s.id!).toList() ?? const [];
|
||||
|
||||
final allReleases = newReleases.value?.items;
|
||||
final userArtistReleases = allReleases?.where((album) {
|
||||
return album.artists?.any((artist) => userArtists.contains(artist.id!)) ==
|
||||
true;
|
||||
}).toList();
|
||||
|
||||
if (userArtistReleases?.isEmpty == true) {
|
||||
return allReleases?.toList() ?? [];
|
||||
}
|
||||
return userArtistReleases ?? [];
|
||||
});
|
||||
|
||||
@ -30,7 +30,7 @@ class AlbumTracksNotifier extends FamilyPaginatedAsyncNotifier<TrackSimple,
|
||||
|
||||
@override
|
||||
fetch(arg, offset, limit) async {
|
||||
final tracks = await spotify.albums.tracks(arg).getPage(offset, limit);
|
||||
final tracks = await spotify.albums.tracks(arg).getPage(limit, offset);
|
||||
return tracks.items?.toList() ?? [];
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ class ArtistAlbumsNotifier extends FamilyPaginatedAsyncNotifier<AlbumSimple,
|
||||
final market = ref.read(userPreferencesProvider).recommendationMarket;
|
||||
final albums = await spotify.artists
|
||||
.albums(arg, country: market)
|
||||
.getPage(offset, limit);
|
||||
.getPage(limit, offset);
|
||||
|
||||
return albums.items?.toList() ?? [];
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ class CategoryPlaylistsNotifier extends FamilyPaginatedAsyncNotifier<
|
||||
(json) => json == null ? null : PlaylistSimple.fromJson(json),
|
||||
'playlists',
|
||||
(json) => PlaylistsFeatured.fromJson(json),
|
||||
).getPage(offset, limit);
|
||||
).getPage(limit, offset);
|
||||
|
||||
return playlists.items?.whereNotNull().toList() ?? [];
|
||||
}
|
||||
|
||||
@ -84,6 +84,6 @@ class PlaylistNotifier extends FamilyAsyncNotifier<Playlist, String> {
|
||||
}
|
||||
|
||||
final playlistProvider =
|
||||
AsyncNotifierProvider.family<PlaylistNotifier, String, PlaylistNotifier>(
|
||||
AsyncNotifierProviderFamily<PlaylistNotifier, Playlist, String>(
|
||||
() => PlaylistNotifier(),
|
||||
);
|
||||
|
||||
@ -20,6 +20,7 @@ import 'package:spotube/provider/spotify_provider.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
|
||||
part 'album/favorite.dart';
|
||||
part 'album/tracks.dart';
|
||||
@ -58,3 +59,4 @@ part 'utils/mixin.dart';
|
||||
part 'utils/state.dart';
|
||||
part 'utils/provider.dart';
|
||||
part 'utils/persistence.dart';
|
||||
part 'utils/async.dart';
|
||||
|
||||
6
lib/provider/spotify/utils/async.dart
Normal file
6
lib/provider/spotify/utils/async.dart
Normal file
@ -0,0 +1,6 @@
|
||||
part of '../spotify.dart';
|
||||
|
||||
extension PaginationExtension<T> on AsyncValue<T> {
|
||||
bool get isLoadingAndEmpty => value == null && isLoading;
|
||||
bool get isLoadingNextPage => value != null && isLoading;
|
||||
}
|
||||
@ -6,17 +6,19 @@ abstract class PaginatedAsyncNotifier<K, T extends BasePaginatedState<K, int>>
|
||||
|
||||
Future<void> fetchMore() async {
|
||||
if (state.value == null || !state.value!.hasMore) return;
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
await update(
|
||||
(state) async {
|
||||
final items = await fetch(state.offset + state.limit, state.limit);
|
||||
return state.copyWith(
|
||||
hasMore: items.length == state.limit,
|
||||
state = await AsyncValue.guard(
|
||||
() async {
|
||||
final items = await fetch(
|
||||
state.value!.offset + state.value!.limit, state.value!.limit);
|
||||
return state.value!.copyWith(
|
||||
hasMore: items.length == state.value!.limit,
|
||||
items: [
|
||||
...state.items,
|
||||
...state.value!.items,
|
||||
...items,
|
||||
],
|
||||
offset: state.offset + state.limit,
|
||||
offset: state.value!.offset + state.value!.limit,
|
||||
) as T;
|
||||
},
|
||||
);
|
||||
@ -31,13 +33,15 @@ abstract class CursorPaginatedAsyncNotifier<K,
|
||||
Future<void> fetchMore() async {
|
||||
if (state.value == null || !state.value!.hasMore) return;
|
||||
|
||||
await update(
|
||||
(state) async {
|
||||
final items = await fetch(state.offset, state.limit);
|
||||
return state.copyWith(
|
||||
hasMore: items.$1.length == state.limit,
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
state = await AsyncValue.guard(
|
||||
() async {
|
||||
final items = await fetch(state.value!.offset, state.value!.limit);
|
||||
return state.value!.copyWith(
|
||||
hasMore: items.$1.length == state.value!.limit,
|
||||
items: [
|
||||
...state.items,
|
||||
...state.value!.items,
|
||||
...items.$1,
|
||||
],
|
||||
offset: items.$2,
|
||||
@ -56,20 +60,22 @@ abstract class FamilyPaginatedAsyncNotifier<
|
||||
Future<void> fetchMore() async {
|
||||
if (state.value == null || !state.value!.hasMore) return;
|
||||
|
||||
await update(
|
||||
(state) async {
|
||||
state = const AsyncLoading();
|
||||
|
||||
state = await AsyncValue.guard(
|
||||
() async {
|
||||
final items = await fetch(
|
||||
arg,
|
||||
state.offset + state.limit,
|
||||
state.limit,
|
||||
state.value!.offset + state.value!.limit,
|
||||
state.value!.limit,
|
||||
);
|
||||
return state.copyWith(
|
||||
hasMore: items.length == state.limit,
|
||||
return state.value!.copyWith(
|
||||
hasMore: items.length == state.value!.limit,
|
||||
items: [
|
||||
...state.items,
|
||||
...state.value!.items,
|
||||
...items,
|
||||
],
|
||||
offset: state.offset + state.limit,
|
||||
offset: state.value!.offset + state.value!.limit,
|
||||
) as T;
|
||||
},
|
||||
);
|
||||
@ -89,17 +95,15 @@ abstract class FamilyCursorPaginatedAsyncNotifier<
|
||||
Future<void> fetchMore() async {
|
||||
if (state.value == null || !state.value!.hasMore) return;
|
||||
|
||||
await update(
|
||||
(state) async {
|
||||
final items = await fetch(
|
||||
arg,
|
||||
state.offset,
|
||||
state.limit,
|
||||
);
|
||||
return state.copyWith(
|
||||
hasMore: items.$1.length == state.limit,
|
||||
state = const AsyncLoading();
|
||||
|
||||
state = await AsyncValue.guard(
|
||||
() async {
|
||||
final items = await fetch(arg, state.value!.offset, state.value!.limit);
|
||||
return state.value!.copyWith(
|
||||
hasMore: items.$1.length == state.value!.limit,
|
||||
items: [
|
||||
...state.items,
|
||||
...state.value!.items,
|
||||
...items.$1,
|
||||
],
|
||||
offset: items.$2,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user