mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-05-08 16:24:36 +00:00
feat: use new providers for playlist and albums screen
This commit is contained in:
parent
dc999b81f0
commit
84cb8d7988
@ -1,18 +1,18 @@
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/services/queries/queries.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
|
||||||
bool useIsUserPlaylist(WidgetRef ref, String playlistId) {
|
bool useIsUserPlaylist(WidgetRef ref, String playlistId) {
|
||||||
final userPlaylistsQuery = useQueries.playlist.ofMineAll(ref);
|
final userPlaylistsQuery = ref.watch(favoritePlaylistsProvider);
|
||||||
final me = useQueries.user.me(ref);
|
final me = ref.watch(meProvider);
|
||||||
|
|
||||||
return useMemoized(
|
return useMemoized(
|
||||||
() =>
|
() =>
|
||||||
userPlaylistsQuery.data?.any((e) =>
|
userPlaylistsQuery.value?.items.any((e) =>
|
||||||
e.id == playlistId &&
|
e.id == playlistId &&
|
||||||
me.data != null &&
|
me.value != null &&
|
||||||
e.owner?.id == me.data?.id) ??
|
e.owner?.id == me.value?.id) ??
|
||||||
false,
|
false,
|
||||||
[userPlaylistsQuery.data, playlistId, me.data],
|
[userPlaylistsQuery.value, playlistId, me.value],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,10 @@
|
|||||||
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/shared/tracks_view/track_view.dart';
|
import 'package:spotube/components/shared/tracks_view/track_view.dart';
|
||||||
import 'package:spotube/components/shared/tracks_view/track_view_props.dart';
|
import 'package:spotube/components/shared/tracks_view/track_view_props.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/infinite_query.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
|
||||||
import 'package:spotube/services/mutations/mutations.dart';
|
|
||||||
import 'package:spotube/services/queries/queries.dart';
|
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
|
|
||||||
class AlbumPage extends HookConsumerWidget {
|
class AlbumPage extends HookConsumerWidget {
|
||||||
@ -21,26 +16,10 @@ class AlbumPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final spotify = ref.watch(spotifyProvider);
|
final tracks = ref.watch(albumTracksProvider(album));
|
||||||
final tracksQuery = useQueries.album.tracksOf(ref, album);
|
final tracksNotifier = ref.watch(albumTracksProvider(album).notifier);
|
||||||
|
final favoriteAlbumsNotifier = ref.watch(favoriteAlbumsProvider.notifier);
|
||||||
final tracks = useMemoized(() {
|
final isSavedAlbum = ref.watch(albumsIsSavedProvider(album.id!));
|
||||||
return tracksQuery.pages.expand((element) => element).toList();
|
|
||||||
}, [tracksQuery.pages]);
|
|
||||||
|
|
||||||
final client = useQueryClient();
|
|
||||||
|
|
||||||
final albumIsSaved = useQueries.album.isSavedForMe(ref, album.id!);
|
|
||||||
final isLiked = albumIsSaved.data ?? false;
|
|
||||||
|
|
||||||
final toggleAlbumLike = useMutations.album.toggleFavorite(
|
|
||||||
ref,
|
|
||||||
album.id!,
|
|
||||||
refreshQueries: [albumIsSaved.key],
|
|
||||||
onData: (_, __) async {
|
|
||||||
await client.refreshInfiniteQueryAllPages("current-user-albums");
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return InheritedTrackView(
|
return InheritedTrackView(
|
||||||
collectionId: album.id!,
|
collectionId: album.id!,
|
||||||
@ -51,29 +30,33 @@ class AlbumPage extends HookConsumerWidget {
|
|||||||
title: album.name!,
|
title: album.name!,
|
||||||
description:
|
description:
|
||||||
"${context.l10n.released} • ${album.releaseDate} • ${album.artists!.first.name}",
|
"${context.l10n.released} • ${album.releaseDate} • ${album.artists!.first.name}",
|
||||||
tracks: tracks,
|
tracks: tracks.value?.items ?? [],
|
||||||
pagination: PaginationProps.fromQuery(
|
pagination: PaginationProps(
|
||||||
tracksQuery,
|
hasNextPage: tracks.value?.hasMore ?? false,
|
||||||
onFetchAll: () {
|
isLoading: tracks.isLoading,
|
||||||
return tracksQuery.fetchAllTracks(getAllTracks: () async {
|
onFetchMore: () async {
|
||||||
final res = await spotify.albums.tracks(album.id!).all();
|
await tracksNotifier.fetchMore();
|
||||||
|
},
|
||||||
return res
|
onFetchAll: () async {
|
||||||
.map((track) =>
|
return tracksNotifier.fetchAll();
|
||||||
TypeConversionUtils.simpleTrack_X_Track(track, album))
|
},
|
||||||
.toList();
|
onRefresh: () async {
|
||||||
});
|
ref.invalidate(albumTracksProvider(album));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
routePath: "/album/${album.id}",
|
routePath: "/album/${album.id}",
|
||||||
shareUrl: album.externalUrls!.spotify!,
|
shareUrl: album.externalUrls!.spotify!,
|
||||||
isLiked: isLiked,
|
isLiked: isSavedAlbum.value ?? false,
|
||||||
onHeart: albumIsSaved.hasData
|
onHeart: isSavedAlbum.value == null
|
||||||
? () async {
|
? null
|
||||||
await toggleAlbumLike.mutate(isLiked);
|
: () async {
|
||||||
return null;
|
if (isSavedAlbum.value!) {
|
||||||
|
await favoriteAlbumsNotifier.removeFavorites([album.id!]);
|
||||||
|
} else {
|
||||||
|
await favoriteAlbumsNotifier.addFavorites([album.id!]);
|
||||||
}
|
}
|
||||||
: null,
|
return null;
|
||||||
|
},
|
||||||
child: const TrackView(),
|
child: const TrackView(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/shared/tracks_view/track_view.dart';
|
import 'package:spotube/components/shared/tracks_view/track_view.dart';
|
||||||
import 'package:spotube/components/shared/tracks_view/track_view_props.dart';
|
import 'package:spotube/components/shared/tracks_view/track_view_props.dart';
|
||||||
import 'package:spotube/services/queries/queries.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
|
||||||
class LikedPlaylistPage extends HookConsumerWidget {
|
class LikedPlaylistPage extends HookConsumerWidget {
|
||||||
final PlaylistSimple playlist;
|
final PlaylistSimple playlist;
|
||||||
@ -14,8 +14,8 @@ class LikedPlaylistPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final likedTracks = useQueries.playlist.likedTracksQuery(ref);
|
final likedTracks = ref.watch(likedTracksProvider);
|
||||||
final tracks = likedTracks.data ?? <Track>[];
|
final tracks = likedTracks.value ?? <Track>[];
|
||||||
|
|
||||||
return InheritedTrackView(
|
return InheritedTrackView(
|
||||||
collectionId: playlist.id!,
|
collectionId: playlist.id!,
|
||||||
@ -28,7 +28,7 @@ class LikedPlaylistPage extends HookConsumerWidget {
|
|||||||
return tracks.toList();
|
return tracks.toList();
|
||||||
},
|
},
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await likedTracks.refresh();
|
ref.invalidate(likedTracksProvider);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: playlist.name!,
|
title: playlist.name!,
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart' hide Page;
|
import 'package:flutter/material.dart' hide Page;
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/shared/dialogs/prompt_dialog.dart';
|
import 'package:spotube/components/shared/dialogs/prompt_dialog.dart';
|
||||||
@ -7,10 +6,7 @@ import 'package:spotube/components/shared/tracks_view/sections/body/use_is_user_
|
|||||||
import 'package:spotube/components/shared/tracks_view/track_view.dart';
|
import 'package:spotube/components/shared/tracks_view/track_view.dart';
|
||||||
import 'package:spotube/components/shared/tracks_view/track_view_props.dart';
|
import 'package:spotube/components/shared/tracks_view/track_view_props.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/infinite_query.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
|
||||||
import 'package:spotube/services/mutations/mutations.dart';
|
|
||||||
import 'package:spotube/services/queries/queries.dart';
|
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
|
|
||||||
class PlaylistPage extends HookConsumerWidget {
|
class PlaylistPage extends HookConsumerWidget {
|
||||||
@ -22,31 +18,13 @@ class PlaylistPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final spotify = ref.watch(spotifyProvider);
|
final tracks = ref.watch(playlistTracksProvider(playlist.id!));
|
||||||
final tracksQuery = useQueries.playlist.tracksOfQuery(ref, playlist.id!);
|
final tracksNotifier =
|
||||||
|
ref.watch(playlistTracksProvider(playlist.id!).notifier);
|
||||||
final tracks = useMemoized(
|
final isFavoritePlaylist =
|
||||||
() {
|
ref.watch(isFavoritePlaylistProvider(playlist.id!));
|
||||||
return tracksQuery.pages.expand((page) => page).toList();
|
final favoritePlaylistsNotifier =
|
||||||
},
|
ref.watch(favoritePlaylistsProvider.notifier);
|
||||||
[tracksQuery.pages],
|
|
||||||
);
|
|
||||||
|
|
||||||
final me = useQueries.user.me(ref);
|
|
||||||
|
|
||||||
final isLikedQuery = useQueries.playlist.doesUserFollow(
|
|
||||||
ref,
|
|
||||||
playlist.id!,
|
|
||||||
me.data?.id ?? '',
|
|
||||||
);
|
|
||||||
|
|
||||||
final togglePlaylistLike = useMutations.playlist.toggleFavorite(
|
|
||||||
ref,
|
|
||||||
playlist.id!,
|
|
||||||
refreshQueries: [
|
|
||||||
isLikedQuery.key,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
final isUserPlaylist = useIsUserPlaylist(ref, playlist.id!);
|
final isUserPlaylist = useIsUserPlaylist(ref, playlist.id!);
|
||||||
|
|
||||||
@ -56,29 +34,26 @@ class PlaylistPage extends HookConsumerWidget {
|
|||||||
playlist.images,
|
playlist.images,
|
||||||
placeholder: ImagePlaceholder.collection,
|
placeholder: ImagePlaceholder.collection,
|
||||||
),
|
),
|
||||||
pagination: PaginationProps.fromQuery(
|
pagination: PaginationProps(
|
||||||
tracksQuery,
|
hasNextPage: tracks.value?.hasMore ?? false,
|
||||||
onFetchAll: () {
|
isLoading: tracks.isLoading,
|
||||||
return tracksQuery.fetchAllTracks(
|
onFetchMore: tracksNotifier.fetchMore,
|
||||||
getAllTracks: () async {
|
onRefresh: () async {
|
||||||
final res = await spotify.playlists
|
ref.invalidate(playlistTracksProvider(playlist.id!));
|
||||||
.getTracksByPlaylistId(playlist.id!)
|
|
||||||
.all();
|
|
||||||
return res.toList();
|
|
||||||
},
|
},
|
||||||
);
|
onFetchAll: () async {
|
||||||
|
return await tracksNotifier.fetchAll();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: playlist.name!,
|
title: playlist.name!,
|
||||||
description: playlist.description,
|
description: playlist.description,
|
||||||
tracks: tracks,
|
tracks: tracks.value?.items ?? [],
|
||||||
routePath: '/playlist/${playlist.id}',
|
routePath: '/playlist/${playlist.id}',
|
||||||
isLiked: isLikedQuery.data ?? false,
|
isLiked: isFavoritePlaylist.value ?? false,
|
||||||
shareUrl: playlist.externalUrls?.spotify ?? "",
|
shareUrl: playlist.externalUrls?.spotify ?? "",
|
||||||
onHeart: () async {
|
onHeart: isFavoritePlaylist.value == null
|
||||||
if (!isLikedQuery.hasData || togglePlaylistLike.isMutating) {
|
? null
|
||||||
return false;
|
: () async {
|
||||||
}
|
|
||||||
final confirmed = isUserPlaylist
|
final confirmed = isUserPlaylist
|
||||||
? await showPromptDialog(
|
? await showPromptDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -86,11 +61,14 @@ class PlaylistPage extends HookConsumerWidget {
|
|||||||
message: context.l10n.delete_playlist_confirmation,
|
message: context.l10n.delete_playlist_confirmation,
|
||||||
)
|
)
|
||||||
: true;
|
: true;
|
||||||
if (confirmed) {
|
if (!confirmed) return null;
|
||||||
await togglePlaylistLike.mutate(isLikedQuery.data!);
|
|
||||||
return isUserPlaylist;
|
if (isFavoritePlaylist.value!) {
|
||||||
|
await favoritePlaylistsNotifier.removeFavorite(playlist);
|
||||||
|
} else {
|
||||||
|
await favoritePlaylistsNotifier.addFavorite(playlist);
|
||||||
}
|
}
|
||||||
return null;
|
return isUserPlaylist;
|
||||||
},
|
},
|
||||||
child: const TrackView(),
|
child: const TrackView(),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -55,6 +55,10 @@ class FavoriteAlbumNotifier
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (final id in ids) {
|
||||||
|
ref.invalidate(albumsIsSavedProvider(id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeFavorites(List<String> ids) async {
|
Future<void> removeFavorites(List<String> ids) async {
|
||||||
@ -69,6 +73,10 @@ class FavoriteAlbumNotifier
|
|||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (final id in ids) {
|
||||||
|
ref.invalidate(albumsIsSavedProvider(id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
part of '../spotify.dart';
|
part of '../spotify.dart';
|
||||||
|
|
||||||
class AlbumTracksState extends PaginatedState<TrackSimple> {
|
class AlbumTracksState extends PaginatedState<Track> {
|
||||||
AlbumTracksState({
|
AlbumTracksState({
|
||||||
required super.items,
|
required super.items,
|
||||||
required super.offset,
|
required super.offset,
|
||||||
@ -10,7 +10,7 @@ class AlbumTracksState extends PaginatedState<TrackSimple> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
AlbumTracksState copyWith({
|
AlbumTracksState copyWith({
|
||||||
List<TrackSimple>? items,
|
List<Track>? items,
|
||||||
int? offset,
|
int? offset,
|
||||||
int? limit,
|
int? limit,
|
||||||
bool? hasMore,
|
bool? hasMore,
|
||||||
@ -24,14 +24,17 @@ class AlbumTracksState extends PaginatedState<TrackSimple> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AlbumTracksNotifier extends FamilyPaginatedAsyncNotifier<TrackSimple,
|
class AlbumTracksNotifier
|
||||||
AlbumTracksState, String> {
|
extends FamilyPaginatedAsyncNotifier<Track, AlbumTracksState, AlbumSimple> {
|
||||||
AlbumTracksNotifier() : super();
|
AlbumTracksNotifier() : super();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
fetch(arg, offset, limit) async {
|
fetch(arg, offset, limit) async {
|
||||||
final tracks = await spotify.albums.tracks(arg).getPage(limit, offset);
|
final tracks = await spotify.albums.tracks(arg.id!).getPage(limit, offset);
|
||||||
return tracks.items?.toList() ?? [];
|
return tracks.items
|
||||||
|
?.map((e) => TypeConversionUtils.simpleTrack_X_Track(e, arg))
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -47,7 +50,7 @@ class AlbumTracksNotifier extends FamilyPaginatedAsyncNotifier<TrackSimple,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final albumTracksProvider =
|
final albumTracksProvider = AsyncNotifierProviderFamily<AlbumTracksNotifier,
|
||||||
AsyncNotifierProviderFamily<AlbumTracksNotifier, AlbumTracksState, String>(
|
AlbumTracksState, AlbumSimple>(
|
||||||
() => AlbumTracksNotifier(),
|
() => AlbumTracksNotifier(),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -52,21 +52,25 @@ class FavoritePlaylistsNotifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addFavorite(PlaylistSimple playlist) async {
|
Future<void> addFavorite(PlaylistSimple playlist) async {
|
||||||
update((state) async {
|
await update((state) async {
|
||||||
await spotify.playlists.followPlaylist(playlist.id!);
|
await spotify.playlists.followPlaylist(playlist.id!);
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: [...state.items, playlist],
|
items: [...state.items, playlist],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ref.invalidate(isFavoritePlaylistProvider(playlist.id!));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeFavorite(PlaylistSimple playlist) async {
|
Future<void> removeFavorite(PlaylistSimple playlist) async {
|
||||||
update((state) async {
|
await update((state) async {
|
||||||
await spotify.playlists.unfollowPlaylist(playlist.id!);
|
await spotify.playlists.unfollowPlaylist(playlist.id!);
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: state.items.where((e) => e.id != playlist.id).toList(),
|
items: state.items.where((e) => e.id != playlist.id).toList(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ref.invalidate(isFavoritePlaylistProvider(playlist.id!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,7 @@ class PlaylistNotifier extends FamilyAsyncNotifier<Playlist, String> {
|
|||||||
state.value!.id!,
|
state.value!.id!,
|
||||||
);
|
);
|
||||||
|
|
||||||
ref.refresh(playlistTracksProvider(state.value!.id!));
|
ref.invalidate(playlistTracksProvider(state.value!.id!));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> modify(PlaylistInput input) async {
|
Future<void> modify(PlaylistInput input) async {
|
||||||
|
|||||||
@ -80,6 +80,31 @@ abstract class FamilyPaginatedAsyncNotifier<
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
arg,
|
||||||
|
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 FamilyCursorPaginatedAsyncNotifier<
|
abstract class FamilyCursorPaginatedAsyncNotifier<
|
||||||
@ -111,4 +136,29 @@ abstract class FamilyCursorPaginatedAsyncNotifier<
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
arg,
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user