mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-05-08 16:24:36 +00:00
feat: use providers in artist page
This commit is contained in:
parent
84cb8d7988
commit
786342912b
@ -12,7 +12,7 @@ import 'package:spotube/pages/artist/section/footer.dart';
|
||||
import 'package:spotube/pages/artist/section/header.dart';
|
||||
import 'package:spotube/pages/artist/section/related_artists.dart';
|
||||
import 'package:spotube/pages/artist/section/top_tracks.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
|
||||
class ArtistPage extends HookConsumerWidget {
|
||||
final String artistId;
|
||||
@ -24,7 +24,7 @@ class ArtistPage extends HookConsumerWidget {
|
||||
final scrollController = useScrollController();
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final artistQuery = useQueries.artist.get(ref, artistId);
|
||||
final artistQuery = ref.watch(artistProvider(artistId));
|
||||
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
@ -35,11 +35,11 @@ class ArtistPage extends HookConsumerWidget {
|
||||
),
|
||||
extendBodyBehindAppBar: true,
|
||||
body: Builder(builder: (context) {
|
||||
if (artistQuery.hasError && artistQuery.data == null) {
|
||||
if (artistQuery.hasError && artistQuery.value == null) {
|
||||
return Center(child: Text(artistQuery.error.toString()));
|
||||
}
|
||||
return Skeletonizer(
|
||||
enabled: artistQuery.isLoading,
|
||||
enabled: artistQuery.isLoadingAndEmpty,
|
||||
child: CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
@ -66,11 +66,11 @@ class ArtistPage extends HookConsumerWidget {
|
||||
SliverSafeArea(
|
||||
sliver: ArtistPageRelatedArtists(artistId: artistId),
|
||||
),
|
||||
if (artistQuery.data != null)
|
||||
if (artistQuery.value != null)
|
||||
SliverSafeArea(
|
||||
top: false,
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: ArtistPageFooter(artist: artistQuery.data!),
|
||||
child: ArtistPageFooter(artist: artistQuery.value!),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@ -5,11 +5,11 @@ import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||
import 'package:spotube/extensions/constrains.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class ArtistPageFooter extends HookConsumerWidget {
|
||||
class ArtistPageFooter extends ConsumerWidget {
|
||||
final Artist artist;
|
||||
const ArtistPageFooter({super.key, required this.artist});
|
||||
|
||||
@ -22,8 +22,9 @@ class ArtistPageFooter extends HookConsumerWidget {
|
||||
artist.images,
|
||||
placeholder: ImagePlaceholder.artist,
|
||||
);
|
||||
final summary = useQueries.artist.wikipediaSummary(artist);
|
||||
if (summary.hasError || !summary.hasData) return const SizedBox.shrink();
|
||||
final summary = ref.watch(artistWikipediaSummaryProvider(artist));
|
||||
if (summary.value == null) return const SizedBox.shrink();
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(16),
|
||||
padding: mediaQuery.smAndDown
|
||||
@ -38,9 +39,9 @@ class ArtistPageFooter extends HookConsumerWidget {
|
||||
BlendMode.darken,
|
||||
),
|
||||
image: UniversalImage.imageProvider(
|
||||
summary.data!.thumbnail?.source_ ?? artistImage,
|
||||
height: summary.data!.thumbnail?.height.toDouble(),
|
||||
width: summary.data!.thumbnail?.width.toDouble(),
|
||||
summary.value!.thumbnail?.source_ ?? artistImage,
|
||||
height: summary.value!.thumbnail?.height.toDouble(),
|
||||
width: summary.value!.thumbnail?.width.toDouble(),
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.center,
|
||||
@ -69,7 +70,7 @@ class ArtistPageFooter extends HookConsumerWidget {
|
||||
),
|
||||
const TextSpan(text: '\n\n'),
|
||||
TextSpan(
|
||||
text: summary.data!.extract,
|
||||
text: summary.value!.extract,
|
||||
),
|
||||
TextSpan(
|
||||
text: '\n...read more at wikipedia',
|
||||
@ -81,7 +82,7 @@ class ArtistPageFooter extends HookConsumerWidget {
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () async {
|
||||
await launchUrlString(
|
||||
"http://en.wikipedia.org/wiki?curid=${summary.data?.pageid}",
|
||||
"http://en.wikipedia.org/wiki?curid=${summary.value?.pageid}",
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/collections/fake.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||
@ -14,8 +11,7 @@ import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/hooks/utils/use_breakpoint_value.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/provider/blacklist_provider.dart';
|
||||
import 'package:spotube/provider/spotify_provider.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
import 'package:spotube/utils/primitive_utils.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
|
||||
@ -25,9 +21,8 @@ class ArtistPageHeader extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final queryClient = useQueryClient();
|
||||
final artistQuery = useQueries.artist.get(ref, artistId);
|
||||
final artist = artistQuery.data ?? FakeData.artist;
|
||||
final artistQuery = ref.watch(artistProvider(artistId));
|
||||
final artist = artistQuery.value ?? FakeData.artist;
|
||||
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
@ -43,7 +38,6 @@ class ArtistPageHeader extends HookConsumerWidget {
|
||||
xxl: textTheme.titleMedium,
|
||||
);
|
||||
|
||||
final spotify = ref.read(spotifyProvider);
|
||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||
final blacklist = ref.watch(BlackListNotifier.provider);
|
||||
final isBlackListed = blacklist.contains(
|
||||
@ -143,53 +137,41 @@ class ArtistPageHeader extends HookConsumerWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (auth != null)
|
||||
HookBuilder(
|
||||
builder: (context) {
|
||||
final isFollowingQuery =
|
||||
useQueries.artist.doIFollow(ref, artistId);
|
||||
Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final isFollowingQuery = ref
|
||||
.watch(artistIsFollowingProvider(artist.id!));
|
||||
final followingArtistNotifier =
|
||||
ref.watch(followedArtistsProvider.notifier);
|
||||
|
||||
final followUnfollow = useCallback(() async {
|
||||
try {
|
||||
isFollowingQuery.data!
|
||||
? await spotify.me.unfollow(
|
||||
FollowingType.artist,
|
||||
[artistId],
|
||||
)
|
||||
: await spotify.me.follow(
|
||||
FollowingType.artist,
|
||||
[artistId],
|
||||
return switch (isFollowingQuery) {
|
||||
AsyncData(value: final following) => Builder(
|
||||
builder: (context) {
|
||||
if (following) {
|
||||
return OutlinedButton(
|
||||
onPressed: () async {
|
||||
await followingArtistNotifier
|
||||
.removeArtists([artist.id!]);
|
||||
},
|
||||
child: Text(context.l10n.following),
|
||||
);
|
||||
await isFollowingQuery.refresh();
|
||||
}
|
||||
|
||||
queryClient.refreshInfiniteQueryAllPages(
|
||||
"user-following-artists");
|
||||
} finally {
|
||||
queryClient.refreshQuery(
|
||||
"user-follows-artists-query/$artistId",
|
||||
);
|
||||
}
|
||||
}, [isFollowingQuery]);
|
||||
|
||||
if (isFollowingQuery.isLoading ||
|
||||
!isFollowingQuery.hasData) {
|
||||
return const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (isFollowingQuery.data!) {
|
||||
return OutlinedButton(
|
||||
onPressed: followUnfollow,
|
||||
child: Text(context.l10n.following),
|
||||
);
|
||||
}
|
||||
|
||||
return FilledButton(
|
||||
onPressed: followUnfollow,
|
||||
child: Text(context.l10n.follow),
|
||||
);
|
||||
return FilledButton(
|
||||
onPressed: () async {
|
||||
await followingArtistNotifier
|
||||
.saveArtists([artist.id!]);
|
||||
},
|
||||
child: Text(context.l10n.follow),
|
||||
);
|
||||
},
|
||||
),
|
||||
AsyncError() => const SizedBox(),
|
||||
_ => const SizedBox.square(
|
||||
dimension: 20,
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
};
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/components/artist/artist_card.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
|
||||
class ArtistPageRelatedArtists extends HookConsumerWidget {
|
||||
class ArtistPageRelatedArtists extends ConsumerWidget {
|
||||
final String artistId;
|
||||
const ArtistPageRelatedArtists({
|
||||
super.key,
|
||||
@ -12,38 +12,34 @@ class ArtistPageRelatedArtists extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final relatedArtists = useQueries.artist.relatedArtistsOf(
|
||||
ref,
|
||||
artistId,
|
||||
);
|
||||
final relatedArtists = ref.watch(relatedArtistsProvider(artistId));
|
||||
|
||||
if (relatedArtists.isLoading || !relatedArtists.hasData) {
|
||||
return const SliverToBoxAdapter(
|
||||
child: Center(child: CircularProgressIndicator()));
|
||||
} else if (relatedArtists.hasError) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Text(relatedArtists.error.toString()),
|
||||
return switch (relatedArtists) {
|
||||
AsyncData(value: final artists) => SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
sliver: SliverGrid.builder(
|
||||
itemCount: artists.length,
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 200,
|
||||
mainAxisExtent: 250,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
childAspectRatio: 0.8,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final artist = artists.elementAt(index);
|
||||
return ArtistCard(artist);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
sliver: SliverGrid.builder(
|
||||
itemCount: relatedArtists.data!.length,
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 200,
|
||||
mainAxisExtent: 250,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
childAspectRatio: 0.8,
|
||||
AsyncError(:final error) => SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Text(error.toString()),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final artist = relatedArtists.data!.elementAt(index);
|
||||
return ArtistCard(artist);
|
||||
},
|
||||
),
|
||||
);
|
||||
_ => const SliverToBoxAdapter(
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/shared/track_tile/track_tile.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
|
||||
class ArtistPageTopTracks extends HookConsumerWidget {
|
||||
final String artistId;
|
||||
@ -20,13 +20,10 @@ class ArtistPageTopTracks extends HookConsumerWidget {
|
||||
|
||||
final playlist = ref.watch(ProxyPlaylistNotifier.provider);
|
||||
final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier);
|
||||
final topTracksQuery = useQueries.artist.topTracksOf(
|
||||
ref,
|
||||
artistId,
|
||||
);
|
||||
final topTracksQuery = ref.watch(artistTopTracksProvider(artistId));
|
||||
|
||||
final isPlaylistPlaying = playlist.containsTracks(
|
||||
topTracksQuery.data ?? <Track>[],
|
||||
topTracksQuery.value ?? <Track>[],
|
||||
);
|
||||
|
||||
if (topTracksQuery.hasError) {
|
||||
@ -38,7 +35,7 @@ class ArtistPageTopTracks extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
final topTracks =
|
||||
topTracksQuery.data ?? List.generate(10, (index) => FakeData.track);
|
||||
topTracksQuery.value ?? List.generate(10, (index) => FakeData.track);
|
||||
|
||||
void playPlaylist(List<Track> tracks, {Track? currentTrack}) async {
|
||||
currentTrack ??= tracks.first;
|
||||
|
||||
@ -64,6 +64,29 @@ class FollowedArtistsNotifier
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
for (final id in artistIds) {
|
||||
ref.invalidate(artistIsFollowingProvider(id));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeArtists(List<String> artistIds) async {
|
||||
if (state.value == null) return;
|
||||
await spotify.me.unfollow(FollowingType.artist, artistIds);
|
||||
|
||||
state = await AsyncValue.guard(() async {
|
||||
final artists = state.value!.items.where((artist) {
|
||||
return !artistIds.contains(artist.id);
|
||||
}).toList();
|
||||
|
||||
return state.value!.copyWith(
|
||||
items: artists,
|
||||
);
|
||||
});
|
||||
|
||||
for (final id in artistIds) {
|
||||
ref.invalidate(artistIsFollowingProvider(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
lib/provider/spotify/artist/related.dart
Normal file
9
lib/provider/spotify/artist/related.dart
Normal file
@ -0,0 +1,9 @@
|
||||
part of '../spotify.dart';
|
||||
|
||||
final relatedArtistsProvider =
|
||||
FutureProvider.family<List<Artist>, String>((ref, artistId) async {
|
||||
final spotify = ref.watch(spotifyProvider);
|
||||
final artists = await spotify.artists.relatedArtists(artistId);
|
||||
|
||||
return artists.toList();
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
part of '../spotify.dart';
|
||||
|
||||
final artistTopTracksProvider = FutureProviderFamily<List<TrackSimple>, String>(
|
||||
final artistTopTracksProvider = FutureProviderFamily<List<Track>, String>(
|
||||
(ref, artistId) async {
|
||||
final spotify = ref.watch(spotifyProvider);
|
||||
final market = ref
|
||||
|
||||
12
lib/provider/spotify/artist/wikipedia.dart
Normal file
12
lib/provider/spotify/artist/wikipedia.dart
Normal file
@ -0,0 +1,12 @@
|
||||
part of '../spotify.dart';
|
||||
|
||||
final artistWikipediaSummaryProvider = FutureProvider.autoDispose
|
||||
.family<Summary?, ArtistSimple>((ref, artist) async {
|
||||
final query = artist.name!.replaceAll(" ", "_");
|
||||
final res = await wikipedia.pageContent.pageSummaryTitleGet(query);
|
||||
|
||||
if (res?.type != "standard") {
|
||||
return await wikipedia.pageContent.pageSummaryTitleGet("${query}_(singer)");
|
||||
}
|
||||
return res;
|
||||
});
|
||||
@ -18,9 +18,11 @@ import 'package:spotube/models/spotify_friends.dart';
|
||||
import 'package:spotube/provider/custom_spotify_endpoint_provider.dart';
|
||||
import 'package:spotube/provider/spotify_provider.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/services/wikipedia/wikipedia.dart';
|
||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
import 'package:wikipedia_api/wikipedia_api.dart';
|
||||
|
||||
part 'album/favorite.dart';
|
||||
part 'album/tracks.dart';
|
||||
@ -32,6 +34,8 @@ part 'artist/is_following.dart';
|
||||
part 'artist/following.dart';
|
||||
part 'artist/top_tracks.dart';
|
||||
part 'artist/albums.dart';
|
||||
part 'artist/wikipedia.dart';
|
||||
part 'artist/related.dart';
|
||||
|
||||
part 'category/genres.dart';
|
||||
part 'category/categories.dart';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user