feat: add artist related providers

This commit is contained in:
Kingkor Roy Tirtho 2024-03-13 19:42:31 +06:00
parent 00b2b76037
commit 78b7759f10
13 changed files with 310 additions and 16 deletions

View File

@ -9,6 +9,7 @@
" required super.items,",
" required super.offset,",
" required super.limit,",
" required super.hasMore,",
" });",
" ",
" @override",
@ -16,11 +17,13 @@
" List<${2:Model}>? items,",
" int? offset,",
" int? limit,",
" bool? hasMore,",
" }) {",
" return ${1:Model}State(",
" items: items ?? this.items,",
" offset: offset ?? this.offset,",
" limit: limit ?? this.limit,",
" hasMore: hasMore ?? this.hasMore,",
" );",
" }",
"}"
@ -56,6 +59,7 @@
" required super.items,",
" required super.offset,",
" required super.limit,",
" required super.hasMore,",
" });",
" ",
" @override",
@ -63,11 +67,13 @@
" List<$2>? items,",
" int? offset,",
" int? limit,",
" bool? hasMore,",
" }) {",
" return $1State(",
" items: items ?? this.items,",
" offset: offset ?? this.offset,",
" limit: limit ?? this.limit,",
" hasMore: hasMore ?? this.hasMore,",
" );",
" }",
"}",
@ -122,6 +128,7 @@
" required super.items,",
" required super.offset,",
" required super.limit,",
" required super.hasMore,",
" });",
" ",
" @override",
@ -129,11 +136,13 @@
" List<$2>? items,",
" int? offset,",
" int? limit,",
" bool? hasMore,",
" }) {",
" return $1State(",
" items: items ?? this.items,",
" offset: offset ?? this.offset,",
" limit: limit ?? this.limit,",
" hasMore: hasMore ?? this.hasMore,",
" );",
" }",
"}",

View File

@ -5,18 +5,16 @@ class FavoriteAlbumState extends PaginatedState<AlbumSimple> {
required super.items,
required super.offset,
required super.limit,
required super.hasMore,
});
@override
FavoriteAlbumState copyWith({
items,
offset,
limit,
}) {
FavoriteAlbumState copyWith({items, offset, limit, hasMore}) {
return FavoriteAlbumState(
items: items ?? this.items,
offset: offset ?? this.offset,
limit: limit ?? this.limit,
hasMore: hasMore ?? this.hasMore,
);
}
}
@ -34,10 +32,12 @@ class FavoriteAlbumNotifier
@override
build() async {
ref.watch(spotifyProvider);
final items = await fetch(0, 20);
return FavoriteAlbumState(
items: await fetch(0, 20),
items: items,
offset: 0,
limit: 20,
hasMore: items.length == 20,
);
}

View File

@ -1,5 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/provider/spotify_provider.dart';
part of '../spotify.dart';
final albumsIsSavedProvider = FutureProvider.autoDispose.family<bool, String>(
(ref, albumId) async {

View File

@ -5,6 +5,7 @@ class AlbumReleasesState extends PaginatedState<AlbumSimple> {
required super.items,
required super.offset,
required super.limit,
required super.hasMore,
});
@override
@ -12,11 +13,13 @@ class AlbumReleasesState extends PaginatedState<AlbumSimple> {
List<AlbumSimple>? items,
int? offset,
int? limit,
bool? hasMore,
}) {
return AlbumReleasesState(
items: items ?? this.items,
offset: offset ?? this.offset,
limit: limit ?? this.limit,
hasMore: hasMore ?? this.hasMore,
);
}
}
@ -45,6 +48,7 @@ class AlbumReleasesNotifier
items: albums,
offset: 0,
limit: 20,
hasMore: albums.length == 20,
);
}
}

View File

@ -5,6 +5,7 @@ class AlbumTracksState extends PaginatedState<TrackSimple> {
required super.items,
required super.offset,
required super.limit,
required super.hasMore,
});
@override
@ -12,11 +13,13 @@ class AlbumTracksState extends PaginatedState<TrackSimple> {
List<TrackSimple>? items,
int? offset,
int? limit,
bool? hasMore,
}) {
return AlbumTracksState(
items: items ?? this.items,
offset: offset ?? this.offset,
limit: limit ?? this.limit,
hasMore: hasMore ?? this.hasMore,
);
}
}
@ -39,6 +42,7 @@ class AlbumTracksNotifier extends FamilyPaginatedAsyncNotifier<TrackSimple,
items: tracks,
offset: 0,
limit: 20,
hasMore: tracks.length == 20,
);
}
}

View File

@ -0,0 +1,60 @@
part of '../spotify.dart';
class ArtistAlbumsState extends PaginatedState<AlbumSimple> {
ArtistAlbumsState({
required super.items,
required super.offset,
required super.limit,
required super.hasMore,
});
@override
ArtistAlbumsState copyWith({
List<AlbumSimple>? items,
int? offset,
int? limit,
bool? hasMore,
}) {
return ArtistAlbumsState(
items: items ?? this.items,
offset: offset ?? this.offset,
limit: limit ?? this.limit,
hasMore: hasMore ?? this.hasMore,
);
}
}
class ArtistAlbumsNotifier extends FamilyPaginatedAsyncNotifier<AlbumSimple,
ArtistAlbumsState, String> {
ArtistAlbumsNotifier() : super();
@override
fetch(arg, offset, limit) async {
final market = ref.read(userPreferencesProvider).recommendationMarket;
final albums = await spotify.artists
.albums(arg, country: market)
.getPage(offset, limit);
return albums.items?.toList() ?? [];
}
@override
build(arg) async {
ref.watch(spotifyProvider);
ref.watch(
userPreferencesProvider.select((s) => s.recommendationMarket),
);
final albums = await fetch(arg, 0, 20);
return ArtistAlbumsState(
items: albums,
offset: 0,
limit: 20,
hasMore: albums.length == 20,
);
}
}
final artistAlbumsProvider = AsyncNotifierProviderFamily<ArtistAlbumsNotifier,
ArtistAlbumsState, String>(
() => ArtistAlbumsNotifier(),
);

View File

@ -0,0 +1,7 @@
part of '../spotify.dart';
final artistProvider = FutureProvider.family((ref, String artistId) {
final spotify = ref.watch(spotifyProvider);
return spotify.artists.get(artistId);
});

View File

@ -0,0 +1,81 @@
part of '../spotify.dart';
class FollowedArtistsState extends CursorPaginatedState<ArtistSimple> {
FollowedArtistsState({
required super.items,
required super.offset,
required super.limit,
required super.hasMore,
});
@override
FollowedArtistsState copyWith({
List<ArtistSimple>? items,
String? offset,
int? limit,
bool? hasMore,
}) {
return FollowedArtistsState(
items: items ?? this.items,
offset: offset ?? this.offset,
limit: limit ?? this.limit,
hasMore: hasMore ?? this.hasMore,
);
}
}
class FollowedArtistsNotifier
extends CursorPaginatedAsyncNotifier<ArtistSimple, FollowedArtistsState> {
FollowedArtistsNotifier() : super();
@override
fetch(offset, limit) async {
final artists = await spotify.me.following(FollowingType.artist).getPage(
limit,
offset ?? '',
);
return (artists.items?.toList() ?? [], artists.after);
}
@override
build() async {
ref.watch(spotifyProvider);
final (artists, nextCursor) = await fetch(null, 50);
return FollowedArtistsState(
items: artists,
offset: nextCursor,
limit: 50,
hasMore: artists.length == 50,
);
}
Future<void> saveArtists(List<String> artistIds) async {
if (state.value == null) return;
await spotify.me.follow(FollowingType.artist, artistIds);
state = await AsyncValue.guard(() async {
final artists = await spotify.artists.list(artistIds);
return state.value!.copyWith(
items: [
...state.value!.items,
...artists,
],
);
});
}
}
final followedArtistsProvider =
AsyncNotifierProvider<FollowedArtistsNotifier, FollowedArtistsState>(
() => FollowedArtistsNotifier(),
);
final allFollowedArtistsProvider = FutureProvider<List<ArtistSimple>>(
(ref) async {
final spotify = ref.watch(spotifyProvider);
final artists = await spotify.me.following(FollowingType.artist).all();
return artists.toList();
},
);

View File

@ -0,0 +1,10 @@
part of '../spotify.dart';
final artistIsFollowingProvider = FutureProvider.family(
(ref, String artistId) async {
final spotify = ref.watch(spotifyProvider);
return spotify.me.checkFollowing(FollowingType.artist, [artistId]).then(
(value) => value[artistId] ?? false,
);
},
);

View File

@ -0,0 +1,12 @@
part of '../spotify.dart';
final artistTopTracksProvider = FutureProviderFamily<List<TrackSimple>, String>(
(ref, artistId) async {
final spotify = ref.watch(spotifyProvider);
final market = ref
.watch(userPreferencesProvider.select((s) => s.recommendationMarket));
final tracks = await spotify.artists.topTracks(artistId, market);
return tracks.toList();
},
);

View File

@ -10,6 +10,13 @@ import 'package:spotube/provider/user_preferences/user_preferences_provider.dart
part 'album/favorite.dart';
part 'album/tracks.dart';
part 'album/releases.dart';
part 'album/is_saved.dart';
part 'artist/artist.dart';
part 'artist/is_following.dart';
part 'artist/following.dart';
part 'artist/top_tracks.dart';
part 'artist/albums.dart';
part 'utils/mixin.dart';
part 'utils/state.dart';

View File

@ -1,6 +1,6 @@
part of '../spotify.dart';
abstract class PaginatedAsyncNotifier<K, T extends PaginatedState<K>>
abstract class PaginatedAsyncNotifier<K, T extends BasePaginatedState<K, int>>
extends AsyncNotifier<T> with SpotifyMixin<T> {
Future<List<K>> fetch(int offset, int limit);
@ -11,6 +11,7 @@ abstract class PaginatedAsyncNotifier<K, T extends PaginatedState<K>>
(state) async {
final items = await fetch(state.offset + state.limit, state.limit);
return state.copyWith(
hasMore: items.length == state.limit,
items: [
...state.items,
...items,
@ -22,8 +23,34 @@ abstract class PaginatedAsyncNotifier<K, T extends PaginatedState<K>>
}
}
abstract class FamilyPaginatedAsyncNotifier<K, T extends PaginatedState<K>, A>
extends FamilyAsyncNotifier<T, A> with SpotifyMixin<T> {
abstract class CursorPaginatedAsyncNotifier<K,
T extends CursorPaginatedState<K>> extends AsyncNotifier<T>
with SpotifyMixin<T> {
Future<(List<K> items, String nextCursor)> fetch(String? offset, int limit);
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,
items: [
...state.items,
...items.$1,
],
offset: items.$2,
) as T;
},
);
}
}
abstract class FamilyPaginatedAsyncNotifier<
K,
T extends BasePaginatedState<K, dynamic>,
A> extends FamilyAsyncNotifier<T, A> with SpotifyMixin<T> {
Future<List<K>> fetch(A arg, int offset, int limit);
Future<void> fetchMore() async {
@ -37,6 +64,7 @@ abstract class FamilyPaginatedAsyncNotifier<K, T extends PaginatedState<K>, A>
state.limit,
);
return state.copyWith(
hasMore: items.length == state.limit,
items: [
...state.items,
...items,
@ -47,3 +75,36 @@ abstract class FamilyPaginatedAsyncNotifier<K, T extends PaginatedState<K>, A>
);
}
}
abstract class FamilyCursorPaginatedAsyncNotifier<
K,
T extends CursorPaginatedState<K>,
A> extends FamilyAsyncNotifier<T, A> with SpotifyMixin<T> {
Future<(List<K> items, String nextCursor)> fetch(
A arg,
String? offset,
int limit,
);
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,
items: [
...state.items,
...items.$1,
],
offset: items.$2,
) as T;
},
);
}
}

View File

@ -1,16 +1,56 @@
part of '../spotify.dart';
abstract class PaginatedState<K> {
abstract class BasePaginatedState<K, Cursor> {
final List<K> items;
final int offset;
final Cursor offset;
final int limit;
final bool hasMore;
PaginatedState({
BasePaginatedState({
required this.items,
required this.offset,
required this.limit,
}) : hasMore = items.length >= limit;
required this.hasMore,
});
PaginatedState<K> copyWith({List<K>? items, int? offset, int? limit});
BasePaginatedState<K, Cursor> copyWith({
List<K>? items,
Cursor? offset,
int? limit,
bool? hasMore,
});
}
abstract class PaginatedState<K> extends BasePaginatedState<K, int> {
PaginatedState({
required super.items,
required super.offset,
required super.limit,
required super.hasMore,
});
@override
PaginatedState<K> copyWith({
List<K>? items,
int? offset,
int? limit,
bool? hasMore,
});
}
abstract class CursorPaginatedState<K> extends BasePaginatedState<K, String> {
CursorPaginatedState({
required super.items,
required super.offset,
required super.limit,
required super.hasMore,
});
@override
CursorPaginatedState<K> copyWith({
List<K>? items,
String? offset,
int? limit,
bool? hasMore,
});
}