feat: add album is saved, new releases and tracks providers

This commit is contained in:
Kingkor Roy Tirtho 2024-03-13 18:57:59 +06:00
parent 39d07567c4
commit 00b2b76037
8 changed files with 317 additions and 4 deletions

161
.vscode/snippets.code-snippets vendored Normal file
View File

@ -0,0 +1,161 @@
{
"PaginatedState": {
"scope": "dart",
"prefix": "paginatedState",
"description": "Generate a PaginatedState",
"body": [
"class ${1:Model}State extends PaginatedState<${2:Model}> {",
" ${1:Model}State({",
" required super.items,",
" required super.offset,",
" required super.limit,",
" });",
" ",
" @override",
" ${1:Model}State copyWith({",
" List<${2:Model}>? items,",
" int? offset,",
" int? limit,",
" }) {",
" return ${1:Model}State(",
" items: items ?? this.items,",
" offset: offset ?? this.offset,",
" limit: limit ?? this.limit,",
" );",
" }",
"}"
]
},
"PaginatedAsyncNotifier": {
"scope": "dart",
"prefix": "paginatedAsyncNotifier",
"description": "Generate a PaginatedAsyncNotifier",
"body": [
"class ${1:NotifierName}Notifier extends PaginatedAsyncNotifier<${3:Item}, ${2:Model}State> {",
" ${1:NotifierName}Notifier() : super();",
" ",
" @override",
" fetch(int offset, int limit) async {",
" throw UnimplementedError();",
" }",
" ",
" @override",
" build() async {",
" throw UnimplementedError();",
" }",
"}"
]
},
"PaginaitedNotifierWithState": {
"scope": "dart",
"prefix": "paginatedNotifierWithState",
"description": "Generate a PaginatedNotifier with PaginatedState",
"body": [
"class $1State extends PaginatedState<$2> {",
" $1State({",
" required super.items,",
" required super.offset,",
" required super.limit,",
" });",
" ",
" @override",
" $1State copyWith({",
" List<$2>? items,",
" int? offset,",
" int? limit,",
" }) {",
" return $1State(",
" items: items ?? this.items,",
" offset: offset ?? this.offset,",
" limit: limit ?? this.limit,",
" );",
" }",
"}",
" ",
"class $1Notifier",
" extends PaginatedAsyncNotifier<$2, $1State> {",
" $1Notifier() : super();",
" ",
" @override",
" fetch(int offset, int limit) async {",
" throw UnimplementedError();",
" }",
" ",
" @override",
" build() async {",
" throw UnimplementedError();",
" }",
"}",
" ",
"final ${1/(.*)/${1:/camelcase}/}Provider = AsyncNotifierProvider<$1Notifier, $1State>(",
" ()=> $1Notifier(),",
");"
]
},
"FamilyPaginatedAsyncNotifier": {
"scope": "dart",
"prefix": "familyPaginatedAsyncNotifier",
"description": "Generate a FamilyPaginatedAsyncNotifier",
"body": [
"class ${1:NotifierName}Notifier extends FamilyPaginatedAsyncNotifier<${3:Item}, ${2:Model}State, {$4:Arg}> {",
" ${1:NotifierName}Notifier() : super();",
" ",
" @override",
" fetch(arg, offset, limit) async {",
" throw UnimplementedError();",
" }",
" ",
" @override",
" build(arg) async {",
" throw UnimplementedError();",
" }",
"}"
]
},
"FamilyPaginaitedNotifierWithState": {
"scope": "dart",
"prefix": "familyPaginatedNotifierWithState",
"description": "Generate a FamilyPaginatedAsyncNotifier with PaginatedState",
"body": [
"class $1State extends PaginatedState<$2> {",
" $1State({",
" required super.items,",
" required super.offset,",
" required super.limit,",
" });",
" ",
" @override",
" $1State copyWith({",
" List<$2>? items,",
" int? offset,",
" int? limit,",
" }) {",
" return $1State(",
" items: items ?? this.items,",
" offset: offset ?? this.offset,",
" limit: limit ?? this.limit,",
" );",
" }",
"}",
" ",
"class $1Notifier",
" extends FamilyPaginatedAsyncNotifier<$2, $1State, $3> {",
" $1Notifier() : super();",
" ",
" @override",
" fetch(arg, offset, limit) async {",
" throw UnimplementedError();",
" }",
" ",
" @override",
" build(arg) async {",
" throw UnimplementedError();",
" }",
"}",
" ",
"final ${1/(.*)/${1:/camelcase}/}Provider = AsyncNotifierProviderFamily<$1Notifier, $1State, $3>(",
" ()=> $1Notifier(),",
");"
]
},
}

View File

@ -22,8 +22,7 @@ class FavoriteAlbumState extends PaginatedState<AlbumSimple> {
} }
class FavoriteAlbumNotifier class FavoriteAlbumNotifier
extends PaginatedAsyncNotifier<AlbumSimple, FavoriteAlbumState> extends PaginatedAsyncNotifier<AlbumSimple, FavoriteAlbumState> {
with SpotifyMixin<FavoriteAlbumState> {
@override @override
Future<List<AlbumSimple>> fetch(int offset, int limit) { Future<List<AlbumSimple>> fetch(int offset, int limit) {
return spotify.me return spotify.me
@ -59,3 +58,8 @@ class FavoriteAlbumNotifier
}); });
} }
} }
final favoriteAlbumsProvider =
AsyncNotifierProvider<FavoriteAlbumNotifier, FavoriteAlbumState>(
() => FavoriteAlbumNotifier(),
);

View File

@ -0,0 +1,11 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/provider/spotify_provider.dart';
final albumsIsSavedProvider = FutureProvider.autoDispose.family<bool, String>(
(ref, albumId) async {
final spotify = ref.watch(spotifyProvider);
return spotify.me.containsSavedAlbums([albumId]).then(
(value) => value[albumId] ?? false,
);
},
);

View File

@ -0,0 +1,55 @@
part of '../spotify.dart';
class AlbumReleasesState extends PaginatedState<AlbumSimple> {
AlbumReleasesState({
required super.items,
required super.offset,
required super.limit,
});
@override
AlbumReleasesState copyWith({
List<AlbumSimple>? items,
int? offset,
int? limit,
}) {
return AlbumReleasesState(
items: items ?? this.items,
offset: offset ?? this.offset,
limit: limit ?? this.limit,
);
}
}
class AlbumReleasesNotifier
extends PaginatedAsyncNotifier<AlbumSimple, 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() ?? [];
}
@override
build() async {
ref.watch(spotifyProvider);
ref.watch(
userPreferencesProvider.select((s) => s.recommendationMarket),
);
final albums = await fetch(0, 20);
return AlbumReleasesState(
items: albums,
offset: 0,
limit: 20,
);
}
}
final albumReleasesProvider =
AsyncNotifierProvider<AlbumReleasesNotifier, AlbumReleasesState>(
() => AlbumReleasesNotifier(),
);

View File

@ -0,0 +1,49 @@
part of '../spotify.dart';
class AlbumTracksState extends PaginatedState<TrackSimple> {
AlbumTracksState({
required super.items,
required super.offset,
required super.limit,
});
@override
AlbumTracksState copyWith({
List<TrackSimple>? items,
int? offset,
int? limit,
}) {
return AlbumTracksState(
items: items ?? this.items,
offset: offset ?? this.offset,
limit: limit ?? this.limit,
);
}
}
class AlbumTracksNotifier extends FamilyPaginatedAsyncNotifier<TrackSimple,
AlbumTracksState, String> {
AlbumTracksNotifier() : super();
@override
fetch(arg, offset, limit) async {
final tracks = await spotify.albums.tracks(arg).getPage(offset, limit);
return tracks.items?.toList() ?? [];
}
@override
build(arg) async {
ref.watch(spotifyProvider);
final tracks = await fetch(arg, 0, 20);
return AlbumTracksState(
items: tracks,
offset: 0,
limit: 20,
);
}
}
final albumTracksProvider =
AsyncNotifierProviderFamily<AlbumTracksNotifier, AlbumTracksState, String>(
() => AlbumTracksNotifier(),
);

View File

@ -2,9 +2,15 @@ library spotify;
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
// ignore: depend_on_referenced_packages, implementation_imports
import 'package:riverpod/src/async_notifier.dart';
import 'package:spotube/provider/spotify_provider.dart'; import 'package:spotube/provider/spotify_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
part 'album/favorite.dart'; part 'album/favorite.dart';
part 'album/tracks.dart';
part 'album/releases.dart';
part 'utils/mixin.dart'; part 'utils/mixin.dart';
part 'utils/state.dart'; part 'utils/state.dart';
part 'utils/provider.dart'; part 'utils/provider.dart';

View File

@ -1,5 +1,6 @@
part of '../spotify.dart'; part of '../spotify.dart';
mixin SpotifyMixin<T> on AsyncNotifier<T> { // ignore: invalid_use_of_internal_member
mixin SpotifyMixin<T> on BuildlessAsyncNotifier<T> {
SpotifyApi get spotify => ref.read(spotifyProvider); SpotifyApi get spotify => ref.read(spotifyProvider);
} }

View File

@ -1,7 +1,7 @@
part of '../spotify.dart'; part of '../spotify.dart';
abstract class PaginatedAsyncNotifier<K, T extends PaginatedState<K>> abstract class PaginatedAsyncNotifier<K, T extends PaginatedState<K>>
extends AsyncNotifier<T> { extends AsyncNotifier<T> with SpotifyMixin<T> {
Future<List<K>> fetch(int offset, int limit); Future<List<K>> fetch(int offset, int limit);
Future<void> fetchMore() async { Future<void> fetchMore() async {
@ -21,3 +21,29 @@ abstract class PaginatedAsyncNotifier<K, T extends PaginatedState<K>>
); );
} }
} }
abstract class FamilyPaginatedAsyncNotifier<K, T extends PaginatedState<K>, A>
extends FamilyAsyncNotifier<T, A> with SpotifyMixin<T> {
Future<List<K>> fetch(A arg, int 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,
state.limit,
);
return state.copyWith(
items: [
...state.items,
...items,
],
offset: state.offset + state.limit,
) as T;
},
);
}
}