mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
refactor: query and mutation jobs as separate abstract class
This commit is contained in:
parent
7c25e1cc8a
commit
42d284f8d8
@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:spotify/spotify.dart' hide Search;
|
import 'package:spotify/spotify.dart' hide Search;
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
import 'package:spotube/components/shared/spotube_page_route.dart';
|
||||||
import 'package:spotube/pages/album/album.dart';
|
import 'package:spotube/pages/album/album.dart';
|
||||||
import 'package:spotube/pages/artist/artist.dart';
|
import 'package:spotube/pages/artist/artist.dart';
|
||||||
import 'package:spotube/pages/genre/genres.dart';
|
import 'package:spotube/pages/genre/genres.dart';
|
||||||
@ -12,9 +14,7 @@ import 'package:spotube/pages/player/player.dart';
|
|||||||
import 'package:spotube/pages/playlist/playlist.dart';
|
import 'package:spotube/pages/playlist/playlist.dart';
|
||||||
import 'package:spotube/pages/root/root_app.dart';
|
import 'package:spotube/pages/root/root_app.dart';
|
||||||
import 'package:spotube/pages/search/search.dart';
|
import 'package:spotube/pages/search/search.dart';
|
||||||
import 'package:spotube/components/shared/spotube_page_route.dart';
|
|
||||||
import 'package:spotube/pages/settings/settings.dart';
|
import 'package:spotube/pages/settings/settings.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
|
||||||
import 'package:spotube/pages/mobile_login/mobile_login.dart';
|
import 'package:spotube/pages/mobile_login/mobile_login.dart';
|
||||||
|
|
||||||
final rootNavigatorKey = GlobalKey<NavigatorState>();
|
final rootNavigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
@ -9,7 +9,7 @@ import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart'
|
|||||||
import 'package:spotube/components/shared/waypoint.dart';
|
import 'package:spotube/components/shared/waypoint.dart';
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
class ArtistAlbumList extends HookConsumerWidget {
|
class ArtistAlbumList extends HookConsumerWidget {
|
||||||
final String artistId;
|
final String artistId;
|
||||||
@ -24,7 +24,7 @@ class ArtistAlbumList extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final scrollController = useScrollController();
|
final scrollController = useScrollController();
|
||||||
final albumsQuery = useInfiniteQuery(
|
final albumsQuery = useInfiniteQuery(
|
||||||
job: artistAlbumsQueryJob(artistId),
|
job: Queries.artist.albumsOf(artistId),
|
||||||
externalData: ref.watch(spotifyProvider),
|
externalData: ref.watch(spotifyProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import 'package:spotube/components/shared/waypoint.dart';
|
|||||||
import 'package:spotube/components/playlist/playlist_card.dart';
|
import 'package:spotube/components/playlist/playlist_card.dart';
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
class CategoryCard extends HookConsumerWidget {
|
class CategoryCard extends HookConsumerWidget {
|
||||||
final Category category;
|
final Category category;
|
||||||
@ -28,7 +28,7 @@ class CategoryCard extends HookConsumerWidget {
|
|||||||
final scrollController = useScrollController();
|
final scrollController = useScrollController();
|
||||||
final spotify = ref.watch(spotifyProvider);
|
final spotify = ref.watch(spotifyProvider);
|
||||||
final playlistQuery = useInfiniteQuery(
|
final playlistQuery = useInfiniteQuery(
|
||||||
job: categoryPlaylistsQueryJob(category.id!),
|
job: Queries.category.playlistsOf(category.id!),
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
final hasNextPage = playlistQuery.pages.isEmpty
|
final hasNextPage = playlistQuery.pages.isEmpty
|
||||||
|
@ -7,7 +7,8 @@ import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart'
|
|||||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||||
import 'package:spotube/provider/auth_provider.dart';
|
import 'package:spotube/provider/auth_provider.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.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 UserAlbums extends HookConsumerWidget {
|
class UserAlbums extends HookConsumerWidget {
|
||||||
@ -20,7 +21,7 @@ class UserAlbums extends HookConsumerWidget {
|
|||||||
return const AnonymousFallback();
|
return const AnonymousFallback();
|
||||||
}
|
}
|
||||||
final albumsQuery = useQuery(
|
final albumsQuery = useQuery(
|
||||||
job: currentUserAlbumsQueryJob,
|
job: Queries.album.ofMine,
|
||||||
externalData: ref.watch(spotifyProvider),
|
externalData: ref.watch(spotifyProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import 'package:spotube/components/shared/waypoint.dart';
|
|||||||
import 'package:spotube/components/artist/artist_card.dart';
|
import 'package:spotube/components/artist/artist_card.dart';
|
||||||
import 'package:spotube/provider/auth_provider.dart';
|
import 'package:spotube/provider/auth_provider.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
class UserArtists extends HookConsumerWidget {
|
class UserArtists extends HookConsumerWidget {
|
||||||
const UserArtists({Key? key}) : super(key: key);
|
const UserArtists({Key? key}) : super(key: key);
|
||||||
@ -21,7 +21,7 @@ class UserArtists extends HookConsumerWidget {
|
|||||||
return const AnonymousFallback();
|
return const AnonymousFallback();
|
||||||
}
|
}
|
||||||
final artistQuery = useInfiniteQuery(
|
final artistQuery = useInfiniteQuery(
|
||||||
job: currentUserFollowingArtistsQueryJob,
|
job: Queries.artist.followedByMe,
|
||||||
externalData: ref.watch(spotifyProvider),
|
externalData: ref.watch(spotifyProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import 'package:spotube/components/playlist/playlist_card.dart';
|
|||||||
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
||||||
import 'package:spotube/provider/auth_provider.dart';
|
import 'package:spotube/provider/auth_provider.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
class UserPlaylists extends HookConsumerWidget {
|
class UserPlaylists extends HookConsumerWidget {
|
||||||
const UserPlaylists({Key? key}) : super(key: key);
|
const UserPlaylists({Key? key}) : super(key: key);
|
||||||
@ -22,7 +22,7 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final playlistsQuery = useQuery(
|
final playlistsQuery = useQuery(
|
||||||
job: currentUserPlaylistsQueryJob,
|
job: Queries.playlist.ofMine,
|
||||||
externalData: ref.watch(spotifyProvider),
|
externalData: ref.watch(spotifyProvider),
|
||||||
);
|
);
|
||||||
Image image = Image();
|
Image image = Image();
|
||||||
|
@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:platform_ui/platform_ui.dart';
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
import 'package:spotube/components/root/sidebar.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
class PlaylistCreateDialog extends HookConsumerWidget {
|
class PlaylistCreateDialog extends HookConsumerWidget {
|
||||||
const PlaylistCreateDialog({Key? key}) : super(key: key);
|
const PlaylistCreateDialog({Key? key}) : super(key: key);
|
||||||
@ -44,7 +44,7 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
|||||||
)
|
)
|
||||||
.then((_) {
|
.then((_) {
|
||||||
QueryBowl.of(context).refetchQueries([
|
QueryBowl.of(context).refetchQueries([
|
||||||
currentUserPlaylistsQueryJob.queryKey,
|
Queries.playlist.ofMine.queryKey,
|
||||||
]);
|
]);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter/gestures.dart';
|
|||||||
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:macos_ui/macos_ui.dart';
|
import 'package:macos_ui/macos_ui.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart' as FluentUI;
|
import 'package:fluent_ui/fluent_ui.dart' as fluent_ui;
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:spotube/components/player/player_actions.dart';
|
import 'package:spotube/components/player/player_actions.dart';
|
||||||
import 'package:spotube/components/player/player_overlay.dart';
|
import 'package:spotube/components/player/player_overlay.dart';
|
||||||
@ -17,10 +17,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
|
|
||||||
class Player extends HookConsumerWidget {
|
class BottomPlayer extends HookConsumerWidget {
|
||||||
Player({Key? key}) : super(key: key);
|
BottomPlayer({Key? key}) : super(key: key);
|
||||||
|
|
||||||
final logger = getLogger(Player);
|
final logger = getLogger(BottomPlayer);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
Playback playback = ref.watch(playbackProvider);
|
Playback playback = ref.watch(playbackProvider);
|
||||||
@ -59,7 +59,7 @@ class Player extends HookConsumerWidget {
|
|||||||
? Colors.grey[800]
|
? Colors.grey[800]
|
||||||
: Colors.blueGrey[50],
|
: Colors.blueGrey[50],
|
||||||
linux: Theme.of(context).backgroundColor,
|
linux: Theme.of(context).backgroundColor,
|
||||||
windows: FluentUI.FluentTheme.maybeOf(context)?.micaBackgroundColor,
|
windows: fluent_ui.FluentTheme.maybeOf(context)?.micaBackgroundColor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -12,11 +12,12 @@ import 'package:spotube/hooks/use_breakpoints.dart';
|
|||||||
import 'package:spotube/provider/auth_provider.dart';
|
import 'package:spotube/provider/auth_provider.dart';
|
||||||
import 'package:spotube/provider/downloader_provider.dart';
|
import 'package:spotube/provider/downloader_provider.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
|
||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart' as FluentUI;
|
import 'package:fluent_ui/fluent_ui.dart' as fluent_ui;
|
||||||
|
|
||||||
final sidebarExtendedStateProvider = StateProvider<bool?>((ref) => null);
|
final sidebarExtendedStateProvider = StateProvider<bool?>((ref) => null);
|
||||||
|
|
||||||
@ -153,8 +154,8 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
windowsFooterItems: [
|
windowsFooterItems: [
|
||||||
FluentUI.PaneItemAction(
|
fluent_ui.PaneItemAction(
|
||||||
icon: const FluentUI.Icon(FluentUI.FluentIcons.settings),
|
icon: const fluent_ui.Icon(fluent_ui.FluentIcons.settings),
|
||||||
onTap: () => goToSettings(context),
|
onTap: () => goToSettings(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -180,7 +181,7 @@ class SidebarFooter extends HookConsumerWidget {
|
|||||||
child: HookBuilder(
|
child: HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final me = useQuery(
|
final me = useQuery(
|
||||||
job: currentUserQueryJob,
|
job: Queries.user.me,
|
||||||
externalData: ref.watch(spotifyProvider),
|
externalData: ref.watch(spotifyProvider),
|
||||||
);
|
);
|
||||||
final data = me.data;
|
final data = me.data;
|
||||||
|
@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:platform_ui/platform_ui.dart';
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
class PlaylistAddTrackDialog extends HookConsumerWidget {
|
class PlaylistAddTrackDialog extends HookConsumerWidget {
|
||||||
final List<Track> tracks;
|
final List<Track> tracks;
|
||||||
@ -18,11 +18,11 @@ class PlaylistAddTrackDialog extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final spotify = ref.watch(spotifyProvider);
|
final spotify = ref.watch(spotifyProvider);
|
||||||
final userPlaylists = useQuery(
|
final userPlaylists = useQuery(
|
||||||
job: currentUserPlaylistsQueryJob,
|
job: Queries.playlist.ofMine,
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
final me = useQuery(
|
final me = useQuery(
|
||||||
job: currentUserQueryJob,
|
job: Queries.user.me,
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
final filteredPlaylists = userPlaylists.data?.where(
|
final filteredPlaylists = userPlaylists.data?.where(
|
||||||
|
@ -8,7 +8,9 @@ import 'package:spotify/spotify.dart';
|
|||||||
import 'package:spotube/hooks/use_palette_color.dart';
|
import 'package:spotube/hooks/use_palette_color.dart';
|
||||||
import 'package:spotube/provider/auth_provider.dart';
|
import 'package:spotube/provider/auth_provider.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.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';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
@ -49,11 +51,11 @@ class HeartButton extends ConsumerWidget {
|
|||||||
|
|
||||||
Tuple3<bool, Mutation<bool, Tuple2<SpotifyApi, bool>>, Query<User, SpotifyApi>>
|
Tuple3<bool, Mutation<bool, Tuple2<SpotifyApi, bool>>, Query<User, SpotifyApi>>
|
||||||
useTrackToggleLike(Track track, WidgetRef ref) {
|
useTrackToggleLike(Track track, WidgetRef ref) {
|
||||||
final me = useQuery(
|
final me =
|
||||||
job: currentUserQueryJob, externalData: ref.watch(spotifyProvider));
|
useQuery(job: Queries.user.me, externalData: ref.watch(spotifyProvider));
|
||||||
|
|
||||||
final savedTracks = useQuery(
|
final savedTracks = useQuery(
|
||||||
job: playlistTracksQueryJob("user-liked-tracks"),
|
job: Queries.playlist.tracksOf("user-liked-tracks"),
|
||||||
externalData: ref.watch(spotifyProvider),
|
externalData: ref.watch(spotifyProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -63,7 +65,7 @@ Tuple3<bool, Mutation<bool, Tuple2<SpotifyApi, bool>>, Query<User, SpotifyApi>>
|
|||||||
final mounted = useIsMounted();
|
final mounted = useIsMounted();
|
||||||
|
|
||||||
final toggleTrackLike = useMutation<bool, Tuple2<SpotifyApi, bool>>(
|
final toggleTrackLike = useMutation<bool, Tuple2<SpotifyApi, bool>>(
|
||||||
job: toggleFavoriteTrackMutationJob(track.id!),
|
job: Mutations.track.toggleFavorite(track.id!),
|
||||||
onMutate: (variable) {
|
onMutate: (variable) {
|
||||||
savedTracks.setQueryData(
|
savedTracks.setQueryData(
|
||||||
(oldData) {
|
(oldData) {
|
||||||
@ -117,7 +119,7 @@ class TrackHeartButton extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final savedTracks = useQuery(
|
final savedTracks = useQuery(
|
||||||
job: playlistTracksQueryJob("user-liked-tracks"),
|
job: Queries.playlist.tracksOf("user-liked-tracks"),
|
||||||
externalData: ref.watch(spotifyProvider),
|
externalData: ref.watch(spotifyProvider),
|
||||||
);
|
);
|
||||||
final toggler = useTrackToggleLike(track, ref);
|
final toggler = useTrackToggleLike(track, ref);
|
||||||
@ -150,22 +152,23 @@ class PlaylistHeartButton extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final me = useQuery(
|
final me = useQuery(
|
||||||
job: currentUserQueryJob,
|
job: Queries.user.me,
|
||||||
externalData: ref.watch(spotifyProvider),
|
externalData: ref.watch(spotifyProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
final job = playlistIsFollowedQueryJob("${playlist.id}:${me.data?.id}");
|
final job =
|
||||||
|
Queries.playlist.doesUserFollow("${playlist.id}:${me.data?.id}");
|
||||||
final isLikedQuery = useQuery(
|
final isLikedQuery = useQuery(
|
||||||
job: job,
|
job: job,
|
||||||
externalData: ref.watch(spotifyProvider),
|
externalData: ref.watch(spotifyProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
final togglePlaylistLike = useMutation<bool, Tuple2<SpotifyApi, bool>>(
|
final togglePlaylistLike = useMutation<bool, Tuple2<SpotifyApi, bool>>(
|
||||||
job: toggleFavoritePlaylistMutationJob(playlist.id!),
|
job: Mutations.playlist.toggleFavorite(playlist.id!),
|
||||||
onData: (payload, variables, queryContext) {
|
onData: (payload, variables, queryContext) {
|
||||||
isLikedQuery.refetch();
|
isLikedQuery.refetch();
|
||||||
QueryBowl.of(context)
|
QueryBowl.of(context)
|
||||||
.getQuery(currentUserPlaylistsQueryJob.queryKey)
|
.getQuery(Queries.playlist.ofMine.queryKey)
|
||||||
?.refetch();
|
?.refetch();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -182,8 +185,9 @@ class PlaylistHeartButton extends HookConsumerWidget {
|
|||||||
titleImage,
|
titleImage,
|
||||||
).dominantColor;
|
).dominantColor;
|
||||||
|
|
||||||
if (me.isLoading || !me.hasData)
|
if (me.isLoading || !me.hasData) {
|
||||||
return const PlatformCircularProgressIndicator();
|
return const PlatformCircularProgressIndicator();
|
||||||
|
}
|
||||||
|
|
||||||
return HeartButton(
|
return HeartButton(
|
||||||
isLiked: isLikedQuery.data ?? false,
|
isLiked: isLikedQuery.data ?? false,
|
||||||
@ -217,28 +221,29 @@ class AlbumHeartButton extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final spotify = ref.watch(spotifyProvider);
|
final spotify = ref.watch(spotifyProvider);
|
||||||
final me = useQuery(
|
final me = useQuery(
|
||||||
job: currentUserQueryJob,
|
job: Queries.user.me,
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
|
|
||||||
final albumIsSaved = useQuery(
|
final albumIsSaved = useQuery(
|
||||||
job: albumIsSavedForCurrentUserQueryJob(album.id!),
|
job: Queries.album.isSavedForMe(album.id!),
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
final isLiked = albumIsSaved.data ?? false;
|
final isLiked = albumIsSaved.data ?? false;
|
||||||
|
|
||||||
final toggleAlbumLike = useMutation<bool, Tuple2<SpotifyApi, bool>>(
|
final toggleAlbumLike = useMutation<bool, Tuple2<SpotifyApi, bool>>(
|
||||||
job: toggleFavoriteAlbumMutationJob(album.id!),
|
job: Mutations.album.toggleFavorite(album.id!),
|
||||||
onData: (payload, variables, queryContext) {
|
onData: (payload, variables, queryContext) {
|
||||||
albumIsSaved.refetch();
|
albumIsSaved.refetch();
|
||||||
QueryBowl.of(context)
|
QueryBowl.of(context)
|
||||||
.getQuery(currentUserAlbumsQueryJob.queryKey)
|
.getQuery(Queries.album.ofMine.queryKey)
|
||||||
?.refetch();
|
?.refetch();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (me.isLoading || !me.hasData)
|
if (me.isLoading || !me.hasData) {
|
||||||
return const PlatformCircularProgressIndicator();
|
return const PlatformCircularProgressIndicator();
|
||||||
|
}
|
||||||
|
|
||||||
return HeartButton(
|
return HeartButton(
|
||||||
isLiked: isLiked,
|
isLiked: isLiked,
|
||||||
|
@ -16,7 +16,9 @@ import 'package:spotube/models/logger.dart';
|
|||||||
import 'package:spotube/provider/auth_provider.dart';
|
import 'package:spotube/provider/auth_provider.dart';
|
||||||
import 'package:spotube/provider/playback_provider.dart';
|
import 'package:spotube/provider/playback_provider.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.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';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
@ -63,12 +65,12 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
final spotify = ref.watch(spotifyProvider);
|
final spotify = ref.watch(spotifyProvider);
|
||||||
final removingTrack = useState<String?>(null);
|
final removingTrack = useState<String?>(null);
|
||||||
final removeTrack = useMutation<bool, Tuple2<SpotifyApi, String>>(
|
final removeTrack = useMutation<bool, Tuple2<SpotifyApi, String>>(
|
||||||
job: removeTrackFromPlaylistMutationJob(playlistId ?? ""),
|
job: Mutations.playlist.removeTrackOf(playlistId ?? ""),
|
||||||
onData: (payload, variables, ctx) {
|
onData: (payload, variables, ctx) {
|
||||||
if (playlistId == null || !payload) return;
|
if (playlistId == null || !payload) return;
|
||||||
QueryBowl.of(context)
|
QueryBowl.of(context)
|
||||||
.getQuery(
|
.getQuery(
|
||||||
playlistTracksQueryJob(playlistId!).queryKey,
|
Queries.playlist.tracksOf(playlistId!).queryKey,
|
||||||
)
|
)
|
||||||
?.refetch();
|
?.refetch();
|
||||||
},
|
},
|
||||||
|
@ -8,12 +8,12 @@ import 'package:spotube/components/shared/heart_button.dart';
|
|||||||
import 'package:spotube/components/shared/track_table/track_collection_view.dart';
|
import 'package:spotube/components/shared/track_table/track_collection_view.dart';
|
||||||
import 'package:spotube/components/shared/track_table/tracks_table_view.dart';
|
import 'package:spotube/components/shared/track_table/tracks_table_view.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
import 'package:spotube/models/current_playlist.dart';
|
import 'package:spotube/models/current_playlist.dart';
|
||||||
import 'package:spotube/provider/playback_provider.dart';
|
import 'package:spotube/provider/playback_provider.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
|
||||||
|
|
||||||
class AlbumPage extends HookConsumerWidget {
|
class AlbumPage extends HookConsumerWidget {
|
||||||
final AlbumSimple album;
|
final AlbumSimple album;
|
||||||
@ -56,7 +56,7 @@ class AlbumPage extends HookConsumerWidget {
|
|||||||
final SpotifyApi spotify = ref.watch(spotifyProvider);
|
final SpotifyApi spotify = ref.watch(spotifyProvider);
|
||||||
|
|
||||||
final tracksSnapshot = useQuery(
|
final tracksSnapshot = useQuery(
|
||||||
job: albumTracksQueryJob(album.id!),
|
job: Queries.album.tracksOf(album.id!),
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@ import 'package:spotube/models/current_playlist.dart';
|
|||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/provider/playback_provider.dart';
|
import 'package:spotube/provider/playback_provider.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
import 'package:spotube/utils/primitive_utils.dart';
|
import 'package:spotube/utils/primitive_utils.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
body: HookBuilder(
|
body: HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final artistsQuery = useQuery(
|
final artistsQuery = useQuery(
|
||||||
job: artistProfileQueryJob(artistId),
|
job: Queries.artist.get(artistId),
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -130,8 +131,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
HookBuilder(
|
HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final isFollowingQuery = useQuery(
|
final isFollowingQuery = useQuery(
|
||||||
job: currentUserFollowsArtistQueryJob(
|
job: Queries.artist.doIFollow(artistId),
|
||||||
artistId),
|
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -165,8 +165,8 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
QueryBowl.of(context).refetchQueries([
|
QueryBowl.of(context).refetchQueries([
|
||||||
currentUserFollowsArtistQueryJob(
|
Queries.artist
|
||||||
artistId)
|
.doIFollow(artistId)
|
||||||
.queryKey,
|
.queryKey,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -211,7 +211,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
HookBuilder(
|
HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final topTracksQuery = useQuery(
|
final topTracksQuery = useQuery(
|
||||||
job: artistTopTracksQueryJob(artistId),
|
job: Queries.artist.topTracksOf(artistId),
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -311,7 +311,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
HookBuilder(
|
HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final relatedArtists = useQuery(
|
final relatedArtists = useQuery(
|
||||||
job: artistRelatedArtistsQueryJob(artistId),
|
job: Queries.artist.relatedArtistsOf(artistId),
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9,8 +9,9 @@ import 'package:spotube/components/shared/shimmers/shimmer_categories.dart';
|
|||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
import 'package:spotube/components/shared/waypoint.dart';
|
import 'package:spotube/components/shared/waypoint.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
|
||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
class GenrePage extends HookConsumerWidget {
|
class GenrePage extends HookConsumerWidget {
|
||||||
@ -24,7 +25,7 @@ class GenrePage extends HookConsumerWidget {
|
|||||||
userPreferencesProvider.select((s) => s.recommendationMarket),
|
userPreferencesProvider.select((s) => s.recommendationMarket),
|
||||||
);
|
);
|
||||||
final categoriesQuery = useInfiniteQuery(
|
final categoriesQuery = useInfiniteQuery(
|
||||||
job: categoriesQueryJob,
|
job: Queries.category.list,
|
||||||
externalData: {
|
externalData: {
|
||||||
"spotify": spotify,
|
"spotify": spotify,
|
||||||
"recommendationMarket": recommendationMarket,
|
"recommendationMarket": recommendationMarket,
|
||||||
|
@ -7,8 +7,9 @@ import 'package:spotify/spotify.dart';
|
|||||||
import 'package:spotube/components/shared/shimmers/shimmer_lyrics.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_lyrics.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
import 'package:spotube/provider/playback_provider.dart';
|
import 'package:spotube/provider/playback_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
|
||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ class GeniusLyrics extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
Playback playback = ref.watch(playbackProvider);
|
Playback playback = ref.watch(playbackProvider);
|
||||||
final geniusLyricsQuery = useQuery(
|
final geniusLyricsQuery = useQuery(
|
||||||
job: geniusLyricsQueryJob,
|
job: Queries.lyrics.static,
|
||||||
externalData: Tuple2(
|
externalData: Tuple2(
|
||||||
playback.track,
|
playback.track,
|
||||||
ref.watch(userPreferencesProvider).geniusAccessToken,
|
ref.watch(userPreferencesProvider).geniusAccessToken,
|
||||||
|
@ -13,7 +13,8 @@ import 'package:spotube/hooks/use_breakpoints.dart';
|
|||||||
import 'package:spotube/hooks/use_synced_lyrics.dart';
|
import 'package:spotube/hooks/use_synced_lyrics.dart';
|
||||||
import 'package:spotube/provider/playback_provider.dart';
|
import 'package:spotube/provider/playback_provider.dart';
|
||||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
|
|
||||||
final lyricDelayState = StateProvider<Duration>(
|
final lyricDelayState = StateProvider<Duration>(
|
||||||
@ -33,7 +34,7 @@ class SyncedLyrics extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
Playback playback = ref.watch(playbackProvider);
|
Playback playback = ref.watch(playbackProvider);
|
||||||
final timedLyricsQuery = useQuery(
|
final timedLyricsQuery = useQuery(
|
||||||
job: rentanadviserLyricsQueryJob,
|
job: Queries.lyrics.synced,
|
||||||
externalData: playback.track,
|
externalData: playback.track,
|
||||||
);
|
);
|
||||||
final lyricDelay = ref.watch(lyricDelayState);
|
final lyricDelay = ref.watch(lyricDelayState);
|
||||||
|
@ -12,7 +12,8 @@ import 'package:spotube/provider/playback_provider.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
|
|
||||||
@ -61,10 +62,9 @@ class PlaylistView extends HookConsumerWidget {
|
|||||||
|
|
||||||
final breakpoint = useBreakpoints();
|
final breakpoint = useBreakpoints();
|
||||||
|
|
||||||
final meSnapshot =
|
final meSnapshot = useQuery(job: Queries.user.me, externalData: spotify);
|
||||||
useQuery(job: currentUserQueryJob, externalData: spotify);
|
|
||||||
final tracksSnapshot = useQuery(
|
final tracksSnapshot = useQuery(
|
||||||
job: playlistTracksQueryJob(playlist.id!),
|
job: Queries.playlist.tracksOf(playlist.id!),
|
||||||
externalData: spotify,
|
externalData: spotify,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class RootApp extends HookConsumerWidget {
|
|||||||
bottomNavigationBar: Column(
|
bottomNavigationBar: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Player(),
|
BottomPlayer(),
|
||||||
SpotubeNavigationBar(
|
SpotubeNavigationBar(
|
||||||
selectedIndex: index.value,
|
selectedIndex: index.value,
|
||||||
onSelectedIndexChanged: (selectedIndex) {
|
onSelectedIndexChanged: (selectedIndex) {
|
||||||
|
@ -18,7 +18,8 @@ import 'package:spotube/models/current_playlist.dart';
|
|||||||
import 'package:spotube/provider/auth_provider.dart';
|
import 'package:spotube/provider/auth_provider.dart';
|
||||||
import 'package:spotube/provider/playback_provider.dart';
|
import 'package:spotube/provider/playback_provider.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:spotube/utils/primitive_utils.dart';
|
import 'package:spotube/utils/primitive_utils.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
@ -47,16 +48,16 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final searchTrack = useInfiniteQuery(
|
final searchTrack = useInfiniteQuery(
|
||||||
job: searchQueryJob(SearchType.track.key),
|
job: Queries.search.get(SearchType.track.key),
|
||||||
externalData: getVariables());
|
externalData: getVariables());
|
||||||
final searchAlbum = useInfiniteQuery(
|
final searchAlbum = useInfiniteQuery(
|
||||||
job: searchQueryJob(SearchType.album.key),
|
job: Queries.search.get(SearchType.album.key),
|
||||||
externalData: getVariables());
|
externalData: getVariables());
|
||||||
final searchPlaylist = useInfiniteQuery(
|
final searchPlaylist = useInfiniteQuery(
|
||||||
job: searchQueryJob(SearchType.playlist.key),
|
job: Queries.search.get(SearchType.playlist.key),
|
||||||
externalData: getVariables());
|
externalData: getVariables());
|
||||||
final searchArtist = useInfiniteQuery(
|
final searchArtist = useInfiniteQuery(
|
||||||
job: searchQueryJob(SearchType.artist.key),
|
job: Queries.search.get(SearchType.artist.key),
|
||||||
externalData: getVariables());
|
externalData: getVariables());
|
||||||
|
|
||||||
void onSearch() {
|
void onSearch() {
|
||||||
|
@ -1,291 +0,0 @@
|
|||||||
import 'package:fl_query/fl_query.dart';
|
|
||||||
import 'package:spotube/models/lyrics.dart';
|
|
||||||
import 'package:spotube/models/spotube_track.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
final categoriesQueryJob =
|
|
||||||
InfiniteQueryJob<Page<Category>, Map<String, dynamic>, int>(
|
|
||||||
queryKey: "categories-query",
|
|
||||||
initialParam: 0,
|
|
||||||
getNextPageParam: (lastPage, lastParam) => lastPage.nextOffset,
|
|
||||||
getPreviousPageParam: (lastPage, lastParam) => lastPage.nextOffset - 16,
|
|
||||||
refetchOnExternalDataChange: true,
|
|
||||||
task: (queryKey, pageParam, data) async {
|
|
||||||
final SpotifyApi spotify = data["spotify"] as SpotifyApi;
|
|
||||||
final String recommendationMarket = data["recommendationMarket"];
|
|
||||||
final categories = await spotify.categories
|
|
||||||
.list(country: recommendationMarket)
|
|
||||||
.getPage(15, pageParam);
|
|
||||||
|
|
||||||
return categories;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final categoryPlaylistsQueryJob =
|
|
||||||
InfiniteQueryJob.withVariableKey<Page<PlaylistSimple>, SpotifyApi, int>(
|
|
||||||
preQueryKey: "category-playlists",
|
|
||||||
initialParam: 0,
|
|
||||||
getNextPageParam: (lastPage, lastParam) => lastPage.nextOffset,
|
|
||||||
getPreviousPageParam: (lastPage, lastParam) => lastPage.nextOffset - 6,
|
|
||||||
task: (queryKey, pageKey, spotify) {
|
|
||||||
final id = getVariable(queryKey);
|
|
||||||
return (id != "user-featured-playlists"
|
|
||||||
? spotify.playlists.getByCategoryId(id)
|
|
||||||
: spotify.playlists.featured)
|
|
||||||
.getPage(5, pageKey);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final currentUserPlaylistsQueryJob =
|
|
||||||
QueryJob<Iterable<PlaylistSimple>, SpotifyApi>(
|
|
||||||
queryKey: "current-user-playlists",
|
|
||||||
task: (_, spotify) {
|
|
||||||
return spotify.playlists.me.all();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final currentUserAlbumsQueryJob = QueryJob<Iterable<AlbumSimple>, SpotifyApi>(
|
|
||||||
queryKey: "current-user-albums",
|
|
||||||
task: (_, spotify) {
|
|
||||||
return spotify.me.savedAlbums().all();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final currentUserFollowingArtistsQueryJob =
|
|
||||||
InfiniteQueryJob<CursorPage<Artist>, SpotifyApi, String>(
|
|
||||||
queryKey: "user-following-artists",
|
|
||||||
initialParam: "",
|
|
||||||
getNextPageParam: (lastPage, lastParam) => lastPage.after,
|
|
||||||
getPreviousPageParam: (lastPage, lastParam) =>
|
|
||||||
lastPage.metadata.previous ?? "",
|
|
||||||
task: (queryKey, pageKey, spotify) {
|
|
||||||
return spotify.me.following(FollowingType.artist).getPage(15, pageKey);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final artistProfileQueryJob = QueryJob.withVariableKey<Artist, SpotifyApi>(
|
|
||||||
preQueryKey: "artist-profile",
|
|
||||||
task: (queryKey, externalData) =>
|
|
||||||
externalData.artists.get(getVariable(queryKey)),
|
|
||||||
);
|
|
||||||
|
|
||||||
final artistTopTracksQueryJob =
|
|
||||||
QueryJob.withVariableKey<Iterable<Track>, SpotifyApi>(
|
|
||||||
preQueryKey: "artist-top-track-query",
|
|
||||||
task: (queryKey, spotify) {
|
|
||||||
return spotify.artists.getTopTracks(getVariable(queryKey), "US");
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final artistAlbumsQueryJob =
|
|
||||||
InfiniteQueryJob.withVariableKey<Page<Album>, SpotifyApi, int>(
|
|
||||||
preQueryKey: "artist-albums",
|
|
||||||
initialParam: 0,
|
|
||||||
getNextPageParam: (lastPage, lastParam) => lastPage.nextOffset,
|
|
||||||
getPreviousPageParam: (lastPage, lastParam) => lastPage.nextOffset - 6,
|
|
||||||
task: (queryKey, pageKey, spotify) {
|
|
||||||
final id = getVariable(queryKey);
|
|
||||||
return spotify.artists.albums(id).getPage(5, pageKey);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final artistRelatedArtistsQueryJob =
|
|
||||||
QueryJob.withVariableKey<Iterable<Artist>, SpotifyApi>(
|
|
||||||
preQueryKey: "artist-related-artist-query",
|
|
||||||
task: (queryKey, spotify) {
|
|
||||||
return spotify.artists.getRelatedArtists(getVariable(queryKey));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final currentUserFollowsArtistQueryJob =
|
|
||||||
QueryJob.withVariableKey<bool, SpotifyApi>(
|
|
||||||
preQueryKey: "user-follows-artists-query",
|
|
||||||
task: (artistId, spotify) async {
|
|
||||||
final result = await spotify.me.isFollowing(
|
|
||||||
FollowingType.artist,
|
|
||||||
[getVariable(artistId)],
|
|
||||||
);
|
|
||||||
return result.first;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final playlistTracksQueryJob =
|
|
||||||
QueryJob.withVariableKey<List<Track>, SpotifyApi>(
|
|
||||||
preQueryKey: "playlist-tracks",
|
|
||||||
task: (queryKey, spotify) {
|
|
||||||
final id = getVariable(queryKey);
|
|
||||||
return id != "user-liked-tracks"
|
|
||||||
? spotify.playlists.getTracksByPlaylistId(id).all().then(
|
|
||||||
(value) => value.toList(),
|
|
||||||
)
|
|
||||||
: spotify.tracks.me.saved.all().then(
|
|
||||||
(tracks) => tracks.map((e) => e.track!).toList(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final albumTracksQueryJob =
|
|
||||||
QueryJob.withVariableKey<List<TrackSimple>, SpotifyApi>(
|
|
||||||
preQueryKey: "album-tracks",
|
|
||||||
task: (queryKey, spotify) {
|
|
||||||
final id = getVariable(queryKey);
|
|
||||||
return spotify.albums.getTracks(id).all().then((value) => value.toList());
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final currentUserQueryJob = QueryJob<User, SpotifyApi>(
|
|
||||||
queryKey: "current-user",
|
|
||||||
refetchOnExternalDataChange: true,
|
|
||||||
task: (_, spotify) async {
|
|
||||||
final me = await spotify.me.get();
|
|
||||||
if (me.images == null || me.images?.isEmpty == true) {
|
|
||||||
me.images = [
|
|
||||||
Image()
|
|
||||||
..height = 50
|
|
||||||
..width = 50
|
|
||||||
..url = TypeConversionUtils.image_X_UrlString(
|
|
||||||
me.images,
|
|
||||||
placeholder: ImagePlaceholder.artist,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return me;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final playlistIsFollowedQueryJob = QueryJob.withVariableKey<bool, SpotifyApi>(
|
|
||||||
preQueryKey: "playlist-is-followed",
|
|
||||||
task: (queryKey, spotify) {
|
|
||||||
final idMap = getVariable(queryKey).split(":");
|
|
||||||
|
|
||||||
return spotify.playlists.followedBy(idMap.first, [idMap.last]).then(
|
|
||||||
(value) => value.first,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final albumIsSavedForCurrentUserQueryJob =
|
|
||||||
QueryJob.withVariableKey<bool, SpotifyApi>(task: (queryKey, spotify) {
|
|
||||||
return spotify.me
|
|
||||||
.isSavedAlbums([getVariable(queryKey)]).then((value) => value.first);
|
|
||||||
});
|
|
||||||
|
|
||||||
final searchQueryJob = InfiniteQueryJob.withVariableKey<List<Page>,
|
|
||||||
Tuple2<String, SpotifyApi>, int>(
|
|
||||||
preQueryKey: "search-query",
|
|
||||||
initialParam: 0,
|
|
||||||
enabled: false,
|
|
||||||
getNextPageParam: (lastPage, lastParam) =>
|
|
||||||
(lastPage.first.items?.length ?? 0) < 10 ? null : lastParam + 10,
|
|
||||||
getPreviousPageParam: (lastPage, lastParam) => lastParam - 10,
|
|
||||||
task: (queryKey, pageParam, variables) {
|
|
||||||
final queryString = variables.item1;
|
|
||||||
final spotify = variables.item2;
|
|
||||||
if (queryString.isEmpty) return [];
|
|
||||||
final searchType = getVariable(queryKey);
|
|
||||||
return spotify.search.get(
|
|
||||||
queryString,
|
|
||||||
types: [SearchType(searchType)],
|
|
||||||
).getPage(10, pageParam);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final geniusLyricsQueryJob = QueryJob<String, Tuple2<Track?, String>>(
|
|
||||||
queryKey: "genius-lyrics-query",
|
|
||||||
task: (_, externalData) async {
|
|
||||||
final currentTrack = externalData.item1;
|
|
||||||
final geniusAccessToken = externalData.item2;
|
|
||||||
if (currentTrack == null) {
|
|
||||||
return "“Give this player a track to play”\n- S'Challa";
|
|
||||||
}
|
|
||||||
final lyrics = await ServiceUtils.getLyrics(
|
|
||||||
currentTrack.name!,
|
|
||||||
currentTrack.artists?.map((s) => s.name).whereNotNull().toList() ?? [],
|
|
||||||
apiKey: geniusAccessToken,
|
|
||||||
optimizeQuery: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (lyrics == null) throw Exception("Unable find lyrics");
|
|
||||||
return lyrics;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final rentanadviserLyricsQueryJob = QueryJob<SubtitleSimple, SpotubeTrack?>(
|
|
||||||
queryKey: "synced-lyrics",
|
|
||||||
task: (_, currentTrack) async {
|
|
||||||
if (currentTrack == null) throw "No track currently";
|
|
||||||
|
|
||||||
final timedLyrics = await ServiceUtils.getTimedLyrics(currentTrack);
|
|
||||||
if (timedLyrics == null) throw Exception("Unable to find lyrics");
|
|
||||||
|
|
||||||
return timedLyrics;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final toggleFavoriteTrackMutationJob =
|
|
||||||
MutationJob.withVariableKey<bool, Tuple2<SpotifyApi, bool>>(
|
|
||||||
preMutationKey: "toggle-track-like",
|
|
||||||
task: (queryKey, externalData) async {
|
|
||||||
final trackId = getVariable(queryKey);
|
|
||||||
final spotify = externalData.item1;
|
|
||||||
final isLiked = externalData.item2;
|
|
||||||
|
|
||||||
if (isLiked) {
|
|
||||||
await spotify.tracks.me.removeOne(trackId);
|
|
||||||
} else {
|
|
||||||
await spotify.tracks.me.saveOne(trackId);
|
|
||||||
}
|
|
||||||
return !isLiked;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final toggleFavoritePlaylistMutationJob =
|
|
||||||
MutationJob.withVariableKey<bool, Tuple2<SpotifyApi, bool>>(
|
|
||||||
preMutationKey: "toggle-playlist-like",
|
|
||||||
task: (queryKey, externalData) async {
|
|
||||||
final playlistId = getVariable(queryKey);
|
|
||||||
final spotify = externalData.item1;
|
|
||||||
final isLiked = externalData.item2;
|
|
||||||
|
|
||||||
if (isLiked) {
|
|
||||||
await spotify.playlists.unfollowPlaylist(playlistId);
|
|
||||||
} else {
|
|
||||||
await spotify.playlists.followPlaylist(playlistId);
|
|
||||||
}
|
|
||||||
return !isLiked;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final toggleFavoriteAlbumMutationJob =
|
|
||||||
MutationJob.withVariableKey<bool, Tuple2<SpotifyApi, bool>>(
|
|
||||||
preMutationKey: "toggle-album-like",
|
|
||||||
task: (queryKey, externalData) async {
|
|
||||||
final albumId = getVariable(queryKey);
|
|
||||||
final spotify = externalData.item1;
|
|
||||||
final isLiked = externalData.item2;
|
|
||||||
|
|
||||||
if (isLiked) {
|
|
||||||
await spotify.me.removeAlbums([albumId]);
|
|
||||||
} else {
|
|
||||||
await spotify.me.saveAlbums([albumId]);
|
|
||||||
}
|
|
||||||
return !isLiked;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final removeTrackFromPlaylistMutationJob =
|
|
||||||
MutationJob.withVariableKey<bool, Tuple2<SpotifyApi, String>>(
|
|
||||||
preMutationKey: "remove-track-from-playlist",
|
|
||||||
task: (queryKey, externalData) async {
|
|
||||||
final spotify = externalData.item1;
|
|
||||||
final playlistId = getVariable(queryKey);
|
|
||||||
final trackId = externalData.item2;
|
|
||||||
|
|
||||||
await spotify.playlists.removeTracks([trackId], playlistId);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
);
|
|
22
lib/services/mutations/album.dart
Normal file
22
lib/services/mutations/album.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class AlbumMutations {
|
||||||
|
final toggleFavorite =
|
||||||
|
MutationJob.withVariableKey<bool, Tuple2<SpotifyApi, bool>>(
|
||||||
|
preMutationKey: "toggle-album-like",
|
||||||
|
task: (queryKey, externalData) async {
|
||||||
|
final albumId = getVariable(queryKey);
|
||||||
|
final spotify = externalData.item1;
|
||||||
|
final isLiked = externalData.item2;
|
||||||
|
|
||||||
|
if (isLiked) {
|
||||||
|
await spotify.me.removeAlbums([albumId]);
|
||||||
|
} else {
|
||||||
|
await spotify.me.saveAlbums([albumId]);
|
||||||
|
}
|
||||||
|
return !isLiked;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
9
lib/services/mutations/mutations.dart
Normal file
9
lib/services/mutations/mutations.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import 'package:spotube/services/mutations/album.dart';
|
||||||
|
import 'package:spotube/services/mutations/playlist.dart';
|
||||||
|
import 'package:spotube/services/mutations/track.dart';
|
||||||
|
|
||||||
|
abstract class Mutations {
|
||||||
|
static final playlist = PlaylistMutations();
|
||||||
|
static final album = AlbumMutations();
|
||||||
|
static final track = TrackMutations();
|
||||||
|
}
|
35
lib/services/mutations/playlist.dart
Normal file
35
lib/services/mutations/playlist.dart
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class PlaylistMutations {
|
||||||
|
final toggleFavorite =
|
||||||
|
MutationJob.withVariableKey<bool, Tuple2<SpotifyApi, bool>>(
|
||||||
|
preMutationKey: "toggle-playlist-like",
|
||||||
|
task: (queryKey, externalData) async {
|
||||||
|
final playlistId = getVariable(queryKey);
|
||||||
|
final spotify = externalData.item1;
|
||||||
|
final isLiked = externalData.item2;
|
||||||
|
|
||||||
|
if (isLiked) {
|
||||||
|
await spotify.playlists.unfollowPlaylist(playlistId);
|
||||||
|
} else {
|
||||||
|
await spotify.playlists.followPlaylist(playlistId);
|
||||||
|
}
|
||||||
|
return !isLiked;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final removeTrackOf =
|
||||||
|
MutationJob.withVariableKey<bool, Tuple2<SpotifyApi, String>>(
|
||||||
|
preMutationKey: "remove-track-from-playlist",
|
||||||
|
task: (queryKey, externalData) async {
|
||||||
|
final spotify = externalData.item1;
|
||||||
|
final playlistId = getVariable(queryKey);
|
||||||
|
final trackId = externalData.item2;
|
||||||
|
|
||||||
|
await spotify.playlists.removeTracks([trackId], playlistId);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
22
lib/services/mutations/track.dart
Normal file
22
lib/services/mutations/track.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class TrackMutations {
|
||||||
|
final toggleFavorite =
|
||||||
|
MutationJob.withVariableKey<bool, Tuple2<SpotifyApi, bool>>(
|
||||||
|
preMutationKey: "toggle-track-like",
|
||||||
|
task: (queryKey, externalData) async {
|
||||||
|
final trackId = getVariable(queryKey);
|
||||||
|
final spotify = externalData.item1;
|
||||||
|
final isLiked = externalData.item2;
|
||||||
|
|
||||||
|
if (isLiked) {
|
||||||
|
await spotify.tracks.me.removeOne(trackId);
|
||||||
|
} else {
|
||||||
|
await spotify.tracks.me.saveOne(trackId);
|
||||||
|
}
|
||||||
|
return !isLiked;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
25
lib/services/queries/album.dart
Normal file
25
lib/services/queries/album.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
|
class AlbumQueries {
|
||||||
|
final ofMine = QueryJob<Iterable<AlbumSimple>, SpotifyApi>(
|
||||||
|
queryKey: "current-user-albums",
|
||||||
|
task: (_, spotify) {
|
||||||
|
return spotify.me.savedAlbums().all();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final tracksOf = QueryJob.withVariableKey<List<TrackSimple>, SpotifyApi>(
|
||||||
|
preQueryKey: "album-tracks",
|
||||||
|
task: (queryKey, spotify) {
|
||||||
|
final id = getVariable(queryKey);
|
||||||
|
return spotify.albums.getTracks(id).all().then((value) => value.toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final isSavedForMe =
|
||||||
|
QueryJob.withVariableKey<bool, SpotifyApi>(task: (queryKey, spotify) {
|
||||||
|
return spotify.me
|
||||||
|
.isSavedAlbums([getVariable(queryKey)]).then((value) => value.first);
|
||||||
|
});
|
||||||
|
}
|
59
lib/services/queries/artist.dart
Normal file
59
lib/services/queries/artist.dart
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
|
class ArtistQueries {
|
||||||
|
final get = QueryJob.withVariableKey<Artist, SpotifyApi>(
|
||||||
|
preQueryKey: "artist-profile",
|
||||||
|
task: (queryKey, externalData) =>
|
||||||
|
externalData.artists.get(getVariable(queryKey)),
|
||||||
|
);
|
||||||
|
|
||||||
|
final followedByMe = InfiniteQueryJob<CursorPage<Artist>, SpotifyApi, String>(
|
||||||
|
queryKey: "user-following-artists",
|
||||||
|
initialParam: "",
|
||||||
|
getNextPageParam: (lastPage, lastParam) => lastPage.after,
|
||||||
|
getPreviousPageParam: (lastPage, lastParam) =>
|
||||||
|
lastPage.metadata.previous ?? "",
|
||||||
|
task: (queryKey, pageKey, spotify) {
|
||||||
|
return spotify.me.following(FollowingType.artist).getPage(15, pageKey);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final doIFollow = QueryJob.withVariableKey<bool, SpotifyApi>(
|
||||||
|
preQueryKey: "user-follows-artists-query",
|
||||||
|
task: (artistId, spotify) async {
|
||||||
|
final result = await spotify.me.isFollowing(
|
||||||
|
FollowingType.artist,
|
||||||
|
[getVariable(artistId)],
|
||||||
|
);
|
||||||
|
return result.first;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final topTracksOf = QueryJob.withVariableKey<Iterable<Track>, SpotifyApi>(
|
||||||
|
preQueryKey: "artist-top-track-query",
|
||||||
|
task: (queryKey, spotify) {
|
||||||
|
return spotify.artists.getTopTracks(getVariable(queryKey), "US");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final albumsOf =
|
||||||
|
InfiniteQueryJob.withVariableKey<Page<Album>, SpotifyApi, int>(
|
||||||
|
preQueryKey: "artist-albums",
|
||||||
|
initialParam: 0,
|
||||||
|
getNextPageParam: (lastPage, lastParam) => lastPage.nextOffset,
|
||||||
|
getPreviousPageParam: (lastPage, lastParam) => lastPage.nextOffset - 6,
|
||||||
|
task: (queryKey, pageKey, spotify) {
|
||||||
|
final id = getVariable(queryKey);
|
||||||
|
return spotify.artists.albums(id).getPage(5, pageKey);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final relatedArtistsOf =
|
||||||
|
QueryJob.withVariableKey<Iterable<Artist>, SpotifyApi>(
|
||||||
|
preQueryKey: "artist-related-artist-query",
|
||||||
|
task: (queryKey, spotify) {
|
||||||
|
return spotify.artists.getRelatedArtists(getVariable(queryKey));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
36
lib/services/queries/category.dart
Normal file
36
lib/services/queries/category.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
|
class CategoryQueries {
|
||||||
|
final list = InfiniteQueryJob<Page<Category>, Map<String, dynamic>, int>(
|
||||||
|
queryKey: "categories-query",
|
||||||
|
initialParam: 0,
|
||||||
|
getNextPageParam: (lastPage, lastParam) => lastPage.nextOffset,
|
||||||
|
getPreviousPageParam: (lastPage, lastParam) => lastPage.nextOffset - 16,
|
||||||
|
refetchOnExternalDataChange: true,
|
||||||
|
task: (queryKey, pageParam, data) async {
|
||||||
|
final SpotifyApi spotify = data["spotify"] as SpotifyApi;
|
||||||
|
final String recommendationMarket = data["recommendationMarket"];
|
||||||
|
final categories = await spotify.categories
|
||||||
|
.list(country: recommendationMarket)
|
||||||
|
.getPage(15, pageParam);
|
||||||
|
|
||||||
|
return categories;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final playlistsOf =
|
||||||
|
InfiniteQueryJob.withVariableKey<Page<PlaylistSimple>, SpotifyApi, int>(
|
||||||
|
preQueryKey: "category-playlists",
|
||||||
|
initialParam: 0,
|
||||||
|
getNextPageParam: (lastPage, lastParam) => lastPage.nextOffset,
|
||||||
|
getPreviousPageParam: (lastPage, lastParam) => lastPage.nextOffset - 6,
|
||||||
|
task: (queryKey, pageKey, spotify) {
|
||||||
|
final id = getVariable(queryKey);
|
||||||
|
return (id != "user-featured-playlists"
|
||||||
|
? spotify.playlists.getByCategoryId(id)
|
||||||
|
: spotify.playlists.featured)
|
||||||
|
.getPage(5, pageKey);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
41
lib/services/queries/lyrics.dart
Normal file
41
lib/services/queries/lyrics.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:spotube/models/lyrics.dart';
|
||||||
|
import 'package:spotube/models/spotube_track.dart';
|
||||||
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class LyricsQueries {
|
||||||
|
final static = QueryJob<String, Tuple2<Track?, String>>(
|
||||||
|
queryKey: "genius-lyrics-query",
|
||||||
|
task: (_, externalData) async {
|
||||||
|
final currentTrack = externalData.item1;
|
||||||
|
final geniusAccessToken = externalData.item2;
|
||||||
|
if (currentTrack == null) {
|
||||||
|
return "“Give this player a track to play”\n- S'Challa";
|
||||||
|
}
|
||||||
|
final lyrics = await ServiceUtils.getLyrics(
|
||||||
|
currentTrack.name!,
|
||||||
|
currentTrack.artists?.map((s) => s.name).whereNotNull().toList() ?? [],
|
||||||
|
apiKey: geniusAccessToken,
|
||||||
|
optimizeQuery: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lyrics == null) throw Exception("Unable find lyrics");
|
||||||
|
return lyrics;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final synced = QueryJob<SubtitleSimple, SpotubeTrack?>(
|
||||||
|
queryKey: "synced-lyrics",
|
||||||
|
task: (_, currentTrack) async {
|
||||||
|
if (currentTrack == null) throw "No track currently";
|
||||||
|
|
||||||
|
final timedLyrics = await ServiceUtils.getTimedLyrics(currentTrack);
|
||||||
|
if (timedLyrics == null) throw Exception("Unable to find lyrics");
|
||||||
|
|
||||||
|
return timedLyrics;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
36
lib/services/queries/playlist.dart
Normal file
36
lib/services/queries/playlist.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
|
class PlaylistQueries {
|
||||||
|
final doesUserFollow = QueryJob.withVariableKey<bool, SpotifyApi>(
|
||||||
|
preQueryKey: "playlist-is-followed",
|
||||||
|
task: (queryKey, spotify) {
|
||||||
|
final idMap = getVariable(queryKey).split(":");
|
||||||
|
|
||||||
|
return spotify.playlists.followedBy(idMap.first, [idMap.last]).then(
|
||||||
|
(value) => value.first,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final ofMine = QueryJob<Iterable<PlaylistSimple>, SpotifyApi>(
|
||||||
|
queryKey: "current-user-playlists",
|
||||||
|
task: (_, spotify) {
|
||||||
|
return spotify.playlists.me.all();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final tracksOf = QueryJob.withVariableKey<List<Track>, SpotifyApi>(
|
||||||
|
preQueryKey: "playlist-tracks",
|
||||||
|
task: (queryKey, spotify) {
|
||||||
|
final id = getVariable(queryKey);
|
||||||
|
return id != "user-liked-tracks"
|
||||||
|
? spotify.playlists.getTracksByPlaylistId(id).all().then(
|
||||||
|
(value) => value.toList(),
|
||||||
|
)
|
||||||
|
: spotify.tracks.me.saved.all().then(
|
||||||
|
(tracks) => tracks.map((e) => e.track!).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
17
lib/services/queries/queries.dart
Normal file
17
lib/services/queries/queries.dart
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import 'package:spotube/services/queries/album.dart';
|
||||||
|
import 'package:spotube/services/queries/artist.dart';
|
||||||
|
import 'package:spotube/services/queries/category.dart';
|
||||||
|
import 'package:spotube/services/queries/lyrics.dart';
|
||||||
|
import 'package:spotube/services/queries/playlist.dart';
|
||||||
|
import 'package:spotube/services/queries/search.dart';
|
||||||
|
import 'package:spotube/services/queries/user.dart';
|
||||||
|
|
||||||
|
abstract class Queries {
|
||||||
|
static final album = AlbumQueries();
|
||||||
|
static final artist = ArtistQueries();
|
||||||
|
static final category = CategoryQueries();
|
||||||
|
static final lyrics = LyricsQueries();
|
||||||
|
static final playlist = PlaylistQueries();
|
||||||
|
static final search = SearchQueries();
|
||||||
|
static final user = UserQueries();
|
||||||
|
}
|
25
lib/services/queries/search.dart
Normal file
25
lib/services/queries/search.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class SearchQueries {
|
||||||
|
final get = InfiniteQueryJob.withVariableKey<List<Page>,
|
||||||
|
Tuple2<String, SpotifyApi>, int>(
|
||||||
|
preQueryKey: "search-query",
|
||||||
|
initialParam: 0,
|
||||||
|
enabled: false,
|
||||||
|
getNextPageParam: (lastPage, lastParam) =>
|
||||||
|
(lastPage.first.items?.length ?? 0) < 10 ? null : lastParam + 10,
|
||||||
|
getPreviousPageParam: (lastPage, lastParam) => lastParam - 10,
|
||||||
|
task: (queryKey, pageParam, variables) {
|
||||||
|
final queryString = variables.item1;
|
||||||
|
final spotify = variables.item2;
|
||||||
|
if (queryString.isEmpty) return [];
|
||||||
|
final searchType = getVariable(queryKey);
|
||||||
|
return spotify.search.get(
|
||||||
|
queryString,
|
||||||
|
types: [SearchType(searchType)],
|
||||||
|
).getPage(10, pageParam);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
25
lib/services/queries/user.dart
Normal file
25
lib/services/queries/user.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
|
|
||||||
|
class UserQueries {
|
||||||
|
final me = QueryJob<User, SpotifyApi>(
|
||||||
|
queryKey: "current-user",
|
||||||
|
refetchOnExternalDataChange: true,
|
||||||
|
task: (_, spotify) async {
|
||||||
|
final me = await spotify.me.get();
|
||||||
|
if (me.images == null || me.images?.isEmpty == true) {
|
||||||
|
me.images = [
|
||||||
|
Image()
|
||||||
|
..height = 50
|
||||||
|
..width = 50
|
||||||
|
..url = TypeConversionUtils.image_X_UrlString(
|
||||||
|
me.images,
|
||||||
|
placeholder: ImagePlaceholder.artist,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return me;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user