refactor: use metadata artist object for artist card and artist page

This commit is contained in:
Kingkor Roy Tirtho 2025-06-15 17:30:02 +06:00
parent 4b09f6c96b
commit b8cae569b4
13 changed files with 80 additions and 66 deletions

View File

@ -15,17 +15,20 @@ abstract class FakeData {
..href = "text"
..total = 1;
static final Artist artist = Artist()
..id = "1"
..name = "Wow artist Good!"
..images = [image]
..popularity = 1
..type = "type"
..uri = "uri"
..externalUrls = externalUrls
..genres = ["genre"]
..href = "text"
..followers = followers;
static final SpotubeFullArtistObject artist = SpotubeFullArtistObject(
id: "1",
name: "What an artist",
externalUri: "https://example.com",
followers: 10000,
genres: ["genre"],
images: [
SpotubeImageObject(
height: 100,
width: 100,
url: "https://dummyimage.com/100x100/cfcfcf/cfcfcf.jpg",
),
],
);
static final externalIds = ExternalIds()
..isrc = "text"
@ -40,7 +43,7 @@ abstract class FakeData {
..label = "label"
..popularity = 1
..albumType = AlbumType.album
..artists = [artist]
// ..artists = [artist]
..availableMarkets = [Market.BD]
..externalUrls = externalUrls
..href = "text"
@ -83,7 +86,7 @@ abstract class FakeData {
static final Track track = Track()
..id = "1"
..artists = [artist, artist, artist]
// ..artists = [artist, artist, artist]
// ..album = albumSimple
..availableMarkets = [Market.BD]
..discNumber = 1

View File

@ -4,7 +4,6 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/fake.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/modules/album/album_card.dart';
@ -31,14 +30,16 @@ class HorizontalPlaybuttonCardView<T> extends HookWidget {
}) : assert(
items.every(
(item) =>
item is PlaylistSimple || item is Artist || item is AlbumSimple,
item is SpotubeSimpleAlbumObject ||
item is SpotubeSimplePlaylistObject ||
item is SpotubeFullArtistObject,
),
);
@override
Widget build(BuildContext context) {
final scrollController = useScrollController();
final isArtist = items.every((s) => s is Artist);
final isArtist = items.every((s) => s is SpotubeFullArtistObject);
final scale = context.theme.scaling;
return Padding(
@ -99,11 +100,12 @@ class HorizontalPlaybuttonCardView<T> extends HookWidget {
final item = items[index];
return switch (item) {
PlaylistSimple() =>
SpotubeSimplePlaylistObject() =>
PlaylistCard(item as SpotubeSimplePlaylistObject),
AlbumSimple() =>
SpotubeSimpleAlbumObject() =>
AlbumCard(item as SpotubeSimpleAlbumObject),
Artist() => ArtistCard(item as Artist),
SpotubeFullArtistObject() =>
ArtistCard(item as SpotubeFullArtistObject),
_ => const SizedBox.shrink(),
};
}),

View File

@ -1,8 +1,9 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/metadata_plugin/artist/albums.dart';
import 'package:spotube/provider/spotify/spotify.dart';
class ArtistAlbumList extends HookConsumerWidget {
@ -15,15 +16,15 @@ class ArtistAlbumList extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final albumsQuery = ref.watch(artistAlbumsProvider(artistId));
final albumsQuery = ref.watch(metadataPluginArtistAlbumsProvider(artistId));
final albumsQueryNotifier =
ref.watch(artistAlbumsProvider(artistId).notifier);
ref.watch(metadataPluginArtistAlbumsProvider(artistId).notifier);
final albums = albumsQuery.asData?.value.items ?? [];
final theme = Theme.of(context);
return HorizontalPlaybuttonCardView<Album>(
return HorizontalPlaybuttonCardView<SpotubeSimpleAlbumObject>(
isLoadingNextPage: albumsQuery.isLoadingNextPage,
hasNextPage: albumsQuery.asData?.value.hasMore ?? false,
items: albums,

View File

@ -4,16 +4,15 @@ import 'package:auto_size_text/auto_size_text.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/image.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/blacklist_provider.dart';
class ArtistCard extends HookConsumerWidget {
final Artist artist;
final SpotubeFullArtistObject artist;
const ArtistCard(this.artist, {super.key});
@override
@ -36,18 +35,18 @@ class ArtistCard extends HookConsumerWidget {
width: 180,
child: Button.card(
onPressed: () {
context.navigateTo(ArtistRoute(artistId: artist.id!));
context.navigateTo(ArtistRoute(artistId: artist.id));
},
child: Column(
children: [
Avatar(
initials: artist.name!.trim()[0].toUpperCase(),
initials: artist.name.trim()[0].toUpperCase(),
provider: backgroundImage,
size: 130,
),
const Gap(10),
AutoSizeText(
artist.name!,
artist.name,
maxLines: 2,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,

View File

@ -11,8 +11,9 @@ import 'package:spotube/extensions/context.dart';
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/related_artists.dart';
import 'package:spotube/pages/artist/section/top_tracks.dart';
import 'package:spotube/provider/metadata_plugin/artist/artist.dart';
import 'package:spotube/provider/spotify/spotify.dart';
import 'package:auto_route/auto_route.dart';
@ -31,7 +32,7 @@ class ArtistPage extends HookConsumerWidget {
final scrollController = useScrollController();
final theme = Theme.of(context);
final artistQuery = ref.watch(artistProvider(artistId));
final artistQuery = ref.watch(metadataPluginArtistProvider(artistId));
return SafeArea(
bottom: false,
@ -74,16 +75,16 @@ class ArtistPage extends HookConsumerWidget {
ArtistPageTopTracks(artistId: artistId),
const SliverGap(20),
SliverToBoxAdapter(child: ArtistAlbumList(artistId)),
SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: SliverToBoxAdapter(
child: Text(
context.l10n.fans_also_like,
style: theme.typography.h4,
),
),
),
ArtistPageRelatedArtists(artistId: artistId),
// SliverPadding(
// padding: const EdgeInsets.all(8.0),
// sliver: SliverToBoxAdapter(
// child: Text(
// context.l10n.fans_also_like,
// style: theme.typography.h4,
// ),
// ),
// ),
// ArtistPageRelatedArtists(artistId: artistId),
const SliverGap(20),
if (artistQuery.asData?.value != null)
SliverToBoxAdapter(

View File

@ -1,17 +1,16 @@
import 'package:flutter/gestures.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/image.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/spotify/spotify.dart';
import 'package:url_launcher/url_launcher_string.dart';
class ArtistPageFooter extends ConsumerWidget {
final Artist artist;
final SpotubeFullArtistObject artist;
const ArtistPageFooter({super.key, required this.artist});
@override

View File

@ -8,10 +8,11 @@ import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/image.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/authentication/authentication.dart';
import 'package:spotube/provider/blacklist_provider.dart';
import 'package:spotube/provider/metadata_plugin/artist/artist.dart';
import 'package:spotube/provider/spotify/spotify.dart';
import 'package:spotube/utils/primitive_utils.dart';
@ -21,7 +22,7 @@ class ArtistPageHeader extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final artistQuery = ref.watch(artistProvider(artistId));
final artistQuery = ref.watch(metadataPluginArtistProvider(artistId));
final artist = artistQuery.asData?.value ?? FakeData.artist;
final theme = Theme.of(context);
@ -30,7 +31,7 @@ class ArtistPageHeader extends HookConsumerWidget {
final auth = ref.watch(authenticationProvider);
ref.watch(blacklistProvider);
final blacklistNotifier = ref.watch(blacklistProvider.notifier);
final isBlackListed = blacklistNotifier.containsArtist(artist);
final isBlackListed = /* blacklistNotifier.containsArtist(artist) */ false;
final image = artist.images.asUrlString(
placeholder: ImagePlaceholder.artist,
@ -111,13 +112,11 @@ class ArtistPageHeader extends HookConsumerWidget {
IconButton.ghost(
icon: const Icon(SpotubeIcons.share),
onPressed: () async {
if (artist.externalUrls?.spotify != null) {
await Clipboard.setData(
ClipboardData(
text: artist.externalUrls!.spotify!,
text: artist.externalUri,
),
);
}
if (!context.mounted) return;
@ -199,7 +198,7 @@ class ArtistPageHeader extends HookConsumerWidget {
child: AutoSizeText(
context.l10n.followers(
PrimitiveUtils.toReadableNumber(
artist.followers!.total!.toDouble(),
artist.followers!.toDouble(),
),
),
maxLines: 1,

View File

@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/modules/artist/artist_card.dart';
import 'package:spotube/provider/spotify/spotify.dart';
@Deprecated("Related artists are no longer supported by Spotube")
class ArtistPageRelatedArtists extends ConsumerWidget {
final String artistId;
const ArtistPageRelatedArtists({
@ -28,7 +29,11 @@ class ArtistPageRelatedArtists extends ConsumerWidget {
),
itemBuilder: (context, index) {
final artist = artists.elementAt(index);
return ArtistCard(artist);
return SizedBox(
width: 180,
// child: ArtistCard(artist),
);
// return ArtistCard(artist);
},
),
),

View File

@ -17,7 +17,8 @@ import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart';
import 'package:spotube/components/waypoint.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/authentication/authentication.dart';
import 'package:spotube/provider/metadata_plugin/auth.dart';
import 'package:spotube/provider/metadata_plugin/library/artists.dart';
import 'package:spotube/provider/spotify/spotify.dart';
import 'package:auto_route/auto_route.dart';
@ -28,10 +29,11 @@ class UserArtistsPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final auth = ref.watch(authenticationProvider);
final authenticated = ref.watch(metadataPluginAuthenticatedProvider);
final artistQuery = ref.watch(followedArtistsProvider);
final artistQueryNotifier = ref.watch(followedArtistsProvider.notifier);
final artistQuery = ref.watch(metadataPluginSavedArtistsProvider);
final artistQueryNotifier =
ref.watch(metadataPluginSavedArtistsProvider.notifier);
final searchText = useState('');
@ -43,7 +45,7 @@ class UserArtistsPage extends HookConsumerWidget {
}
return artists
.map((e) => (
weightedRatio(e.name!, searchText.value),
weightedRatio(e.name, searchText.value),
e,
))
.sorted((a, b) => b.$1.compareTo(a.$1))
@ -54,7 +56,7 @@ class UserArtistsPage extends HookConsumerWidget {
final controller = useScrollController();
if (auth.asData?.value == null) {
if (authenticated.asData?.value != true) {
return const AnonymousFallback();
}

View File

@ -24,7 +24,7 @@ class MetadataPluginArtistAlbumNotifier
}
}
final metadataPluginArtistAlbumsProvider = AsyncNotifierFamilyProvider<
final metadataPluginArtistAlbumsProvider = AsyncNotifierProviderFamily<
MetadataPluginArtistAlbumNotifier,
SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>,
String>(

View File

@ -10,10 +10,12 @@ class MetadataPluginSavedArtistNotifier
int offset,
int limit,
) async {
return await (await metadataPlugin).user.savedArtists(
final artists = await (await metadataPlugin).user.savedArtists(
limit: limit,
offset: offset,
);
return artists;
}
@override

View File

@ -1,8 +1,8 @@
part of '../spotify.dart';
final artistWikipediaSummaryProvider = FutureProvider.autoDispose
.family<Summary?, ArtistSimple>((ref, artist) async {
final query = artist.name!.replaceAll(" ", "_");
.family<Summary?, SpotubeFullArtistObject>((ref, artist) async {
final query = artist.name.replaceAll(" ", "_");
final res = await wikipedia.pageContent.pageSummaryTitleGet(query);
if (res?.type != "standard") {

View File

@ -7,6 +7,7 @@ import 'package:drift/drift.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/authentication/authentication.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart';