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:go_router/go_router.dart';
|
||||
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/artist/artist.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/root/root_app.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/utils/platform.dart';
|
||||
import 'package:spotube/pages/mobile_login/mobile_login.dart';
|
||||
|
||||
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/models/logger.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 {
|
||||
final String artistId;
|
||||
@ -24,7 +24,7 @@ class ArtistAlbumList extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
final scrollController = useScrollController();
|
||||
final albumsQuery = useInfiniteQuery(
|
||||
job: artistAlbumsQueryJob(artistId),
|
||||
job: Queries.artist.albumsOf(artistId),
|
||||
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/models/logger.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 {
|
||||
final Category category;
|
||||
@ -28,7 +28,7 @@ class CategoryCard extends HookConsumerWidget {
|
||||
final scrollController = useScrollController();
|
||||
final spotify = ref.watch(spotifyProvider);
|
||||
final playlistQuery = useInfiniteQuery(
|
||||
job: categoryPlaylistsQueryJob(category.id!),
|
||||
job: Queries.category.playlistsOf(category.id!),
|
||||
externalData: spotify,
|
||||
);
|
||||
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/provider/auth_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';
|
||||
|
||||
class UserAlbums extends HookConsumerWidget {
|
||||
@ -20,7 +21,7 @@ class UserAlbums extends HookConsumerWidget {
|
||||
return const AnonymousFallback();
|
||||
}
|
||||
final albumsQuery = useQuery(
|
||||
job: currentUserAlbumsQueryJob,
|
||||
job: Queries.album.ofMine,
|
||||
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/provider/auth_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 {
|
||||
const UserArtists({Key? key}) : super(key: key);
|
||||
@ -21,7 +21,7 @@ class UserArtists extends HookConsumerWidget {
|
||||
return const AnonymousFallback();
|
||||
}
|
||||
final artistQuery = useInfiniteQuery(
|
||||
job: currentUserFollowingArtistsQueryJob,
|
||||
job: Queries.artist.followedByMe,
|
||||
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/provider/auth_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 {
|
||||
const UserPlaylists({Key? key}) : super(key: key);
|
||||
@ -22,7 +22,7 @@ class UserPlaylists extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
final playlistsQuery = useQuery(
|
||||
job: currentUserPlaylistsQueryJob,
|
||||
job: Queries.playlist.ofMine,
|
||||
externalData: ref.watch(spotifyProvider),
|
||||
);
|
||||
Image image = Image();
|
||||
|
@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:platform_ui/platform_ui.dart';
|
||||
import 'package:spotube/components/root/sidebar.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 {
|
||||
const PlaylistCreateDialog({Key? key}) : super(key: key);
|
||||
@ -44,7 +44,7 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
||||
)
|
||||
.then((_) {
|
||||
QueryBowl.of(context).refetchQueries([
|
||||
currentUserPlaylistsQueryJob.queryKey,
|
||||
Queries.playlist.ofMine.queryKey,
|
||||
]);
|
||||
Navigator.pop(context);
|
||||
});
|
||||
|
@ -3,7 +3,7 @@ import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.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:spotube/components/player/player_actions.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/utils/type_conversion_utils.dart';
|
||||
|
||||
class Player extends HookConsumerWidget {
|
||||
Player({Key? key}) : super(key: key);
|
||||
class BottomPlayer extends HookConsumerWidget {
|
||||
BottomPlayer({Key? key}) : super(key: key);
|
||||
|
||||
final logger = getLogger(Player);
|
||||
final logger = getLogger(BottomPlayer);
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
Playback playback = ref.watch(playbackProvider);
|
||||
@ -59,7 +59,7 @@ class Player extends HookConsumerWidget {
|
||||
? Colors.grey[800]
|
||||
: Colors.blueGrey[50],
|
||||
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/downloader_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/services/queries/queries.dart';
|
||||
import 'package:spotube/utils/platform.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);
|
||||
|
||||
@ -153,8 +154,8 @@ class Sidebar extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
windowsFooterItems: [
|
||||
FluentUI.PaneItemAction(
|
||||
icon: const FluentUI.Icon(FluentUI.FluentIcons.settings),
|
||||
fluent_ui.PaneItemAction(
|
||||
icon: const fluent_ui.Icon(fluent_ui.FluentIcons.settings),
|
||||
onTap: () => goToSettings(context),
|
||||
),
|
||||
],
|
||||
@ -180,7 +181,7 @@ class SidebarFooter extends HookConsumerWidget {
|
||||
child: HookBuilder(
|
||||
builder: (context) {
|
||||
final me = useQuery(
|
||||
job: currentUserQueryJob,
|
||||
job: Queries.user.me,
|
||||
externalData: ref.watch(spotifyProvider),
|
||||
);
|
||||
final data = me.data;
|
||||
|
@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:platform_ui/platform_ui.dart';
|
||||
import 'package:spotify/spotify.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 {
|
||||
final List<Track> tracks;
|
||||
@ -18,11 +18,11 @@ class PlaylistAddTrackDialog extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
final spotify = ref.watch(spotifyProvider);
|
||||
final userPlaylists = useQuery(
|
||||
job: currentUserPlaylistsQueryJob,
|
||||
job: Queries.playlist.ofMine,
|
||||
externalData: spotify,
|
||||
);
|
||||
final me = useQuery(
|
||||
job: currentUserQueryJob,
|
||||
job: Queries.user.me,
|
||||
externalData: spotify,
|
||||
);
|
||||
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/provider/auth_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:tuple/tuple.dart';
|
||||
|
||||
@ -49,11 +51,11 @@ class HeartButton extends ConsumerWidget {
|
||||
|
||||
Tuple3<bool, Mutation<bool, Tuple2<SpotifyApi, bool>>, Query<User, SpotifyApi>>
|
||||
useTrackToggleLike(Track track, WidgetRef ref) {
|
||||
final me = useQuery(
|
||||
job: currentUserQueryJob, externalData: ref.watch(spotifyProvider));
|
||||
final me =
|
||||
useQuery(job: Queries.user.me, externalData: ref.watch(spotifyProvider));
|
||||
|
||||
final savedTracks = useQuery(
|
||||
job: playlistTracksQueryJob("user-liked-tracks"),
|
||||
job: Queries.playlist.tracksOf("user-liked-tracks"),
|
||||
externalData: ref.watch(spotifyProvider),
|
||||
);
|
||||
|
||||
@ -63,7 +65,7 @@ Tuple3<bool, Mutation<bool, Tuple2<SpotifyApi, bool>>, Query<User, SpotifyApi>>
|
||||
final mounted = useIsMounted();
|
||||
|
||||
final toggleTrackLike = useMutation<bool, Tuple2<SpotifyApi, bool>>(
|
||||
job: toggleFavoriteTrackMutationJob(track.id!),
|
||||
job: Mutations.track.toggleFavorite(track.id!),
|
||||
onMutate: (variable) {
|
||||
savedTracks.setQueryData(
|
||||
(oldData) {
|
||||
@ -117,7 +119,7 @@ class TrackHeartButton extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final savedTracks = useQuery(
|
||||
job: playlistTracksQueryJob("user-liked-tracks"),
|
||||
job: Queries.playlist.tracksOf("user-liked-tracks"),
|
||||
externalData: ref.watch(spotifyProvider),
|
||||
);
|
||||
final toggler = useTrackToggleLike(track, ref);
|
||||
@ -150,22 +152,23 @@ class PlaylistHeartButton extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final me = useQuery(
|
||||
job: currentUserQueryJob,
|
||||
job: Queries.user.me,
|
||||
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(
|
||||
job: job,
|
||||
externalData: ref.watch(spotifyProvider),
|
||||
);
|
||||
|
||||
final togglePlaylistLike = useMutation<bool, Tuple2<SpotifyApi, bool>>(
|
||||
job: toggleFavoritePlaylistMutationJob(playlist.id!),
|
||||
job: Mutations.playlist.toggleFavorite(playlist.id!),
|
||||
onData: (payload, variables, queryContext) {
|
||||
isLikedQuery.refetch();
|
||||
QueryBowl.of(context)
|
||||
.getQuery(currentUserPlaylistsQueryJob.queryKey)
|
||||
.getQuery(Queries.playlist.ofMine.queryKey)
|
||||
?.refetch();
|
||||
},
|
||||
);
|
||||
@ -182,8 +185,9 @@ class PlaylistHeartButton extends HookConsumerWidget {
|
||||
titleImage,
|
||||
).dominantColor;
|
||||
|
||||
if (me.isLoading || !me.hasData)
|
||||
if (me.isLoading || !me.hasData) {
|
||||
return const PlatformCircularProgressIndicator();
|
||||
}
|
||||
|
||||
return HeartButton(
|
||||
isLiked: isLikedQuery.data ?? false,
|
||||
@ -217,28 +221,29 @@ class AlbumHeartButton extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
final spotify = ref.watch(spotifyProvider);
|
||||
final me = useQuery(
|
||||
job: currentUserQueryJob,
|
||||
job: Queries.user.me,
|
||||
externalData: spotify,
|
||||
);
|
||||
|
||||
final albumIsSaved = useQuery(
|
||||
job: albumIsSavedForCurrentUserQueryJob(album.id!),
|
||||
job: Queries.album.isSavedForMe(album.id!),
|
||||
externalData: spotify,
|
||||
);
|
||||
final isLiked = albumIsSaved.data ?? false;
|
||||
|
||||
final toggleAlbumLike = useMutation<bool, Tuple2<SpotifyApi, bool>>(
|
||||
job: toggleFavoriteAlbumMutationJob(album.id!),
|
||||
job: Mutations.album.toggleFavorite(album.id!),
|
||||
onData: (payload, variables, queryContext) {
|
||||
albumIsSaved.refetch();
|
||||
QueryBowl.of(context)
|
||||
.getQuery(currentUserAlbumsQueryJob.queryKey)
|
||||
.getQuery(Queries.album.ofMine.queryKey)
|
||||
?.refetch();
|
||||
},
|
||||
);
|
||||
|
||||
if (me.isLoading || !me.hasData)
|
||||
if (me.isLoading || !me.hasData) {
|
||||
return const PlatformCircularProgressIndicator();
|
||||
}
|
||||
|
||||
return HeartButton(
|
||||
isLiked: isLiked,
|
||||
|
@ -16,7 +16,9 @@ import 'package:spotube/models/logger.dart';
|
||||
import 'package:spotube/provider/auth_provider.dart';
|
||||
import 'package:spotube/provider/playback_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:tuple/tuple.dart';
|
||||
|
||||
@ -63,12 +65,12 @@ class TrackTile extends HookConsumerWidget {
|
||||
final spotify = ref.watch(spotifyProvider);
|
||||
final removingTrack = useState<String?>(null);
|
||||
final removeTrack = useMutation<bool, Tuple2<SpotifyApi, String>>(
|
||||
job: removeTrackFromPlaylistMutationJob(playlistId ?? ""),
|
||||
job: Mutations.playlist.removeTrackOf(playlistId ?? ""),
|
||||
onData: (payload, variables, ctx) {
|
||||
if (playlistId == null || !payload) return;
|
||||
QueryBowl.of(context)
|
||||
.getQuery(
|
||||
playlistTracksQueryJob(playlistId!).queryKey,
|
||||
Queries.playlist.tracksOf(playlistId!).queryKey,
|
||||
)
|
||||
?.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/tracks_table_view.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/type_conversion_utils.dart';
|
||||
import 'package:spotube/models/current_playlist.dart';
|
||||
import 'package:spotube/provider/playback_provider.dart';
|
||||
import 'package:spotube/provider/spotify_provider.dart';
|
||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
||||
|
||||
class AlbumPage extends HookConsumerWidget {
|
||||
final AlbumSimple album;
|
||||
@ -56,7 +56,7 @@ class AlbumPage extends HookConsumerWidget {
|
||||
final SpotifyApi spotify = ref.watch(spotifyProvider);
|
||||
|
||||
final tracksSnapshot = useQuery(
|
||||
job: albumTracksQueryJob(album.id!),
|
||||
job: Queries.album.tracksOf(album.id!),
|
||||
externalData: spotify,
|
||||
);
|
||||
|
||||
|
@ -18,7 +18,8 @@ import 'package:spotube/models/current_playlist.dart';
|
||||
import 'package:spotube/models/logger.dart';
|
||||
import 'package:spotube/provider/playback_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/type_conversion_utils.dart';
|
||||
|
||||
@ -60,7 +61,7 @@ class ArtistPage extends HookConsumerWidget {
|
||||
body: HookBuilder(
|
||||
builder: (context) {
|
||||
final artistsQuery = useQuery(
|
||||
job: artistProfileQueryJob(artistId),
|
||||
job: Queries.artist.get(artistId),
|
||||
externalData: spotify,
|
||||
);
|
||||
|
||||
@ -130,8 +131,7 @@ class ArtistPage extends HookConsumerWidget {
|
||||
HookBuilder(
|
||||
builder: (context) {
|
||||
final isFollowingQuery = useQuery(
|
||||
job: currentUserFollowsArtistQueryJob(
|
||||
artistId),
|
||||
job: Queries.artist.doIFollow(artistId),
|
||||
externalData: spotify,
|
||||
);
|
||||
|
||||
@ -165,8 +165,8 @@ class ArtistPage extends HookConsumerWidget {
|
||||
);
|
||||
} finally {
|
||||
QueryBowl.of(context).refetchQueries([
|
||||
currentUserFollowsArtistQueryJob(
|
||||
artistId)
|
||||
Queries.artist
|
||||
.doIFollow(artistId)
|
||||
.queryKey,
|
||||
]);
|
||||
}
|
||||
@ -211,7 +211,7 @@ class ArtistPage extends HookConsumerWidget {
|
||||
HookBuilder(
|
||||
builder: (context) {
|
||||
final topTracksQuery = useQuery(
|
||||
job: artistTopTracksQueryJob(artistId),
|
||||
job: Queries.artist.topTracksOf(artistId),
|
||||
externalData: spotify,
|
||||
);
|
||||
|
||||
@ -311,7 +311,7 @@ class ArtistPage extends HookConsumerWidget {
|
||||
HookBuilder(
|
||||
builder: (context) {
|
||||
final relatedArtists = useQuery(
|
||||
job: artistRelatedArtistsQueryJob(artistId),
|
||||
job: Queries.artist.relatedArtistsOf(artistId),
|
||||
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/waypoint.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/services/queries/queries.dart';
|
||||
import 'package:spotube/utils/platform.dart';
|
||||
|
||||
class GenrePage extends HookConsumerWidget {
|
||||
@ -24,7 +25,7 @@ class GenrePage extends HookConsumerWidget {
|
||||
userPreferencesProvider.select((s) => s.recommendationMarket),
|
||||
);
|
||||
final categoriesQuery = useInfiniteQuery(
|
||||
job: categoriesQueryJob,
|
||||
job: Queries.category.list,
|
||||
externalData: {
|
||||
"spotify": spotify,
|
||||
"recommendationMarket": recommendationMarket,
|
||||
|
@ -7,8 +7,9 @@ import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/shared/shimmers/shimmer_lyrics.dart';
|
||||
import 'package:spotube/hooks/use_breakpoints.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/services/queries/queries.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
@ -23,7 +24,7 @@ class GeniusLyrics extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
Playback playback = ref.watch(playbackProvider);
|
||||
final geniusLyricsQuery = useQuery(
|
||||
job: geniusLyricsQueryJob,
|
||||
job: Queries.lyrics.static,
|
||||
externalData: Tuple2(
|
||||
playback.track,
|
||||
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/provider/playback_provider.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';
|
||||
|
||||
final lyricDelayState = StateProvider<Duration>(
|
||||
@ -33,7 +34,7 @@ class SyncedLyrics extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
Playback playback = ref.watch(playbackProvider);
|
||||
final timedLyricsQuery = useQuery(
|
||||
job: rentanadviserLyricsQueryJob,
|
||||
job: Queries.lyrics.synced,
|
||||
externalData: playback.track,
|
||||
);
|
||||
final lyricDelay = ref.watch(lyricDelayState);
|
||||
|
@ -12,7 +12,8 @@ import 'package:spotube/provider/playback_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:spotify/spotify.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/type_conversion_utils.dart';
|
||||
|
||||
@ -61,10 +62,9 @@ class PlaylistView extends HookConsumerWidget {
|
||||
|
||||
final breakpoint = useBreakpoints();
|
||||
|
||||
final meSnapshot =
|
||||
useQuery(job: currentUserQueryJob, externalData: spotify);
|
||||
final meSnapshot = useQuery(job: Queries.user.me, externalData: spotify);
|
||||
final tracksSnapshot = useQuery(
|
||||
job: playlistTracksQueryJob(playlist.id!),
|
||||
job: Queries.playlist.tracksOf(playlist.id!),
|
||||
externalData: spotify,
|
||||
);
|
||||
|
||||
|
@ -75,7 +75,7 @@ class RootApp extends HookConsumerWidget {
|
||||
bottomNavigationBar: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Player(),
|
||||
BottomPlayer(),
|
||||
SpotubeNavigationBar(
|
||||
selectedIndex: index.value,
|
||||
onSelectedIndexChanged: (selectedIndex) {
|
||||
|
@ -18,7 +18,8 @@ import 'package:spotube/models/current_playlist.dart';
|
||||
import 'package:spotube/provider/auth_provider.dart';
|
||||
import 'package:spotube/provider/playback_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/primitive_utils.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
@ -47,16 +48,16 @@ class SearchPage extends HookConsumerWidget {
|
||||
);
|
||||
|
||||
final searchTrack = useInfiniteQuery(
|
||||
job: searchQueryJob(SearchType.track.key),
|
||||
job: Queries.search.get(SearchType.track.key),
|
||||
externalData: getVariables());
|
||||
final searchAlbum = useInfiniteQuery(
|
||||
job: searchQueryJob(SearchType.album.key),
|
||||
job: Queries.search.get(SearchType.album.key),
|
||||
externalData: getVariables());
|
||||
final searchPlaylist = useInfiniteQuery(
|
||||
job: searchQueryJob(SearchType.playlist.key),
|
||||
job: Queries.search.get(SearchType.playlist.key),
|
||||
externalData: getVariables());
|
||||
final searchArtist = useInfiniteQuery(
|
||||
job: searchQueryJob(SearchType.artist.key),
|
||||
job: Queries.search.get(SearchType.artist.key),
|
||||
externalData: getVariables());
|
||||
|
||||
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