mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-05-08 16:24:36 +00:00
feat: add album is saved, new releases and tracks providers
This commit is contained in:
parent
39d07567c4
commit
00b2b76037
161
.vscode/snippets.code-snippets
vendored
Normal file
161
.vscode/snippets.code-snippets
vendored
Normal 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(),",
|
||||||
|
");"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -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(),
|
||||||
|
);
|
||||||
|
|||||||
11
lib/provider/spotify/album/is_saved.dart
Normal file
11
lib/provider/spotify/album/is_saved.dart
Normal 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,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
55
lib/provider/spotify/album/releases.dart
Normal file
55
lib/provider/spotify/album/releases.dart
Normal 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(),
|
||||||
|
);
|
||||||
49
lib/provider/spotify/album/tracks.dart
Normal file
49
lib/provider/spotify/album/tracks.dart
Normal 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(),
|
||||||
|
);
|
||||||
@ -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';
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user