mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
refactor: artist page
This commit is contained in:
parent
bbad701c07
commit
4afe0cca68
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart' hide Page;
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
|
import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
@ -30,7 +30,7 @@ class ArtistAlbumList extends HookConsumerWidget {
|
|||||||
onFetchMore: albumsQueryNotifier.fetchMore,
|
onFetchMore: albumsQueryNotifier.fetchMore,
|
||||||
title: Text(
|
title: Text(
|
||||||
context.l10n.albums,
|
context.l10n.albums,
|
||||||
style: theme.textTheme.headlineSmall,
|
style: theme.typography.h4,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
|
import 'package:spotube/components/button/back_button.dart';
|
||||||
|
|
||||||
import 'package:spotube/components/titlebar/titlebar.dart';
|
import 'package:spotube/components/titlebar/titlebar.dart';
|
||||||
import 'package:spotube/modules/artist/artist_album_list.dart';
|
import 'package:spotube/modules/artist/artist_album_list.dart';
|
||||||
@ -30,12 +30,14 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
return SafeArea(
|
return SafeArea(
|
||||||
bottom: false,
|
bottom: false,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: const TitleBar(
|
headers: const [
|
||||||
leading: [BackButton()],
|
TitleBar(
|
||||||
backgroundColor: Colors.transparent,
|
leading: [BackButton()],
|
||||||
),
|
backgroundColor: Colors.transparent,
|
||||||
extendBodyBehindAppBar: true,
|
)
|
||||||
body: Builder(builder: (context) {
|
],
|
||||||
|
floatingHeader: true,
|
||||||
|
child: Builder(builder: (context) {
|
||||||
if (artistQuery.hasError && artistQuery.asData?.value == null) {
|
if (artistQuery.hasError && artistQuery.asData?.value == null) {
|
||||||
return Center(child: Text(artistQuery.error.toString()));
|
return Center(child: Text(artistQuery.error.toString()));
|
||||||
}
|
}
|
||||||
@ -50,31 +52,26 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
child: ArtistPageHeader(artistId: artistId),
|
child: ArtistPageHeader(artistId: artistId),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SliverGap(50),
|
|
||||||
ArtistPageTopTracks(artistId: artistId),
|
|
||||||
const SliverGap(50),
|
|
||||||
SliverToBoxAdapter(child: ArtistAlbumList(artistId)),
|
|
||||||
const SliverGap(20),
|
const SliverGap(20),
|
||||||
|
ArtistPageTopTracks(artistId: artistId),
|
||||||
|
const SliverGap(20),
|
||||||
|
SliverToBoxAdapter(child: ArtistAlbumList(artistId)),
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
sliver: SliverToBoxAdapter(
|
sliver: SliverToBoxAdapter(
|
||||||
child: Text(
|
child: Text(
|
||||||
context.l10n.fans_also_like,
|
context.l10n.fans_also_like,
|
||||||
style: theme.textTheme.headlineSmall,
|
style: theme.typography.h4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverSafeArea(
|
ArtistPageRelatedArtists(artistId: artistId),
|
||||||
sliver: ArtistPageRelatedArtists(artistId: artistId),
|
const SliverGap(20),
|
||||||
),
|
|
||||||
if (artistQuery.asData?.value != null)
|
if (artistQuery.asData?.value != null)
|
||||||
SliverSafeArea(
|
SliverToBoxAdapter(
|
||||||
top: false,
|
child: ArtistPageFooter(artist: artistQuery.asData!.value),
|
||||||
sliver: SliverToBoxAdapter(
|
|
||||||
child:
|
|
||||||
ArtistPageFooter(artist: artistQuery.asData!.value),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
const SliverSafeArea(sliver: SliverGap(10)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -26,7 +26,7 @@ class ArtistPageFooter extends ConsumerWidget {
|
|||||||
if (summary.asData?.value == null) return const SizedBox.shrink();
|
if (summary.asData?.value == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.all(16),
|
margin: const EdgeInsets.all(8),
|
||||||
padding: mediaQuery.smAndDown
|
padding: mediaQuery.smAndDown
|
||||||
? const EdgeInsets.all(20)
|
? const EdgeInsets.all(20)
|
||||||
: const EdgeInsets.all(30),
|
: const EdgeInsets.all(30),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:gap/gap.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
import 'package:spotube/collections/fake.dart';
|
import 'package:spotube/collections/fake.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
@ -9,7 +9,6 @@ import 'package:spotube/components/image/universal_image.dart';
|
|||||||
import 'package:spotube/extensions/constrains.dart';
|
import 'package:spotube/extensions/constrains.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
import 'package:spotube/hooks/utils/use_breakpoint_value.dart';
|
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/provider/blacklist_provider.dart';
|
import 'package:spotube/provider/blacklist_provider.dart';
|
||||||
@ -25,19 +24,8 @@ class ArtistPageHeader extends HookConsumerWidget {
|
|||||||
final artistQuery = ref.watch(artistProvider(artistId));
|
final artistQuery = ref.watch(artistProvider(artistId));
|
||||||
final artist = artistQuery.asData?.value ?? FakeData.artist;
|
final artist = artistQuery.asData?.value ?? FakeData.artist;
|
||||||
|
|
||||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
|
||||||
final mediaQuery = MediaQuery.of(context);
|
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final ThemeData(:textTheme) = theme;
|
final ThemeData(:typography) = theme;
|
||||||
|
|
||||||
final chipTextVariant = useBreakpointValue(
|
|
||||||
xs: textTheme.bodySmall,
|
|
||||||
sm: textTheme.bodySmall,
|
|
||||||
md: textTheme.bodyMedium,
|
|
||||||
lg: textTheme.bodyLarge,
|
|
||||||
xl: textTheme.titleSmall,
|
|
||||||
xxl: textTheme.titleMedium,
|
|
||||||
);
|
|
||||||
|
|
||||||
final auth = ref.watch(authenticationProvider);
|
final auth = ref.watch(authenticationProvider);
|
||||||
ref.watch(blacklistProvider);
|
ref.watch(blacklistProvider);
|
||||||
@ -48,190 +36,192 @@ class ArtistPageHeader extends HookConsumerWidget {
|
|||||||
placeholder: ImagePlaceholder.artist,
|
placeholder: ImagePlaceholder.artist,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final actions = Skeleton.keep(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (auth.asData?.value != null)
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final isFollowingQuery = ref.watch(
|
||||||
|
artistIsFollowingProvider(artist.id!),
|
||||||
|
);
|
||||||
|
final followingArtistNotifier = ref.watch(
|
||||||
|
followedArtistsProvider.notifier,
|
||||||
|
);
|
||||||
|
|
||||||
|
return switch (isFollowingQuery) {
|
||||||
|
AsyncData(value: final following) => Builder(
|
||||||
|
builder: (context) {
|
||||||
|
if (following) {
|
||||||
|
return Button.outline(
|
||||||
|
onPressed: () async {
|
||||||
|
await followingArtistNotifier
|
||||||
|
.removeArtists([artist.id!]);
|
||||||
|
},
|
||||||
|
child: Text(context.l10n.following),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Button.primary(
|
||||||
|
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),
|
||||||
|
Tooltip(
|
||||||
|
tooltip: TooltipContainer(
|
||||||
|
child: Text(context.l10n.add_artist_to_blacklist),
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
SpotubeIcons.userRemove,
|
||||||
|
color: !isBlackListed ? Colors.red[400] : null,
|
||||||
|
),
|
||||||
|
variance: isBlackListed
|
||||||
|
? ButtonVariance.destructive
|
||||||
|
: ButtonVariance.ghost,
|
||||||
|
onPressed: () async {
|
||||||
|
if (isBlackListed) {
|
||||||
|
await ref.read(blacklistProvider.notifier).remove(artist.id!);
|
||||||
|
} else {
|
||||||
|
await ref.read(blacklistProvider.notifier).add(
|
||||||
|
BlacklistTableCompanion.insert(
|
||||||
|
name: artist.name!,
|
||||||
|
elementId: artist.id!,
|
||||||
|
elementType: BlacklistedType.artist,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton.ghost(
|
||||||
|
icon: const Icon(SpotubeIcons.share),
|
||||||
|
onPressed: () async {
|
||||||
|
if (artist.externalUrls?.spotify != null) {
|
||||||
|
await Clipboard.setData(
|
||||||
|
ClipboardData(
|
||||||
|
text: artist.externalUrls!.spotify!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.mounted) return;
|
||||||
|
|
||||||
|
showToast(
|
||||||
|
context: context,
|
||||||
|
location: ToastLocation.topRight,
|
||||||
|
dismissible: true,
|
||||||
|
builder: (context, overlay) {
|
||||||
|
return SurfaceCard(
|
||||||
|
child: Text(
|
||||||
|
context.l10n.artist_url_copied,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constrains) {
|
builder: (context, constrains) {
|
||||||
return Center(
|
return Padding(
|
||||||
child: Flex(
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Card(
|
||||||
crossAxisAlignment: constrains.smAndDown
|
child: Column(
|
||||||
? CrossAxisAlignment.start
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
: CrossAxisAlignment.center,
|
mainAxisSize: MainAxisSize.min,
|
||||||
direction: constrains.smAndDown ? Axis.vertical : Axis.horizontal,
|
children: [
|
||||||
children: [
|
Row(
|
||||||
DecoratedBox(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
decoration: BoxDecoration(
|
children: [
|
||||||
boxShadow: kElevationToShadow[2],
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(35),
|
borderRadius: theme.borderRadiusXl,
|
||||||
),
|
child: UniversalImage(
|
||||||
child: ClipRRect(
|
path: image,
|
||||||
borderRadius: BorderRadius.circular(35),
|
width: constrains.mdAndUp ? 200 : 120,
|
||||||
child: UniversalImage(
|
height: constrains.mdAndUp ? 200 : 120,
|
||||||
path: image,
|
fit: BoxFit.cover,
|
||||||
width: 250,
|
|
||||||
height: 250,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(20),
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 10, vertical: 5),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.blue,
|
|
||||||
borderRadius: BorderRadius.circular(50)),
|
|
||||||
child: Skeleton.keep(
|
|
||||||
child: Text(
|
|
||||||
artist.type!.toUpperCase(),
|
|
||||||
style: chipTextVariant.copyWith(
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isBlackListed) ...[
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 10, vertical: 5),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.red[400],
|
|
||||||
borderRadius: BorderRadius.circular(50)),
|
|
||||||
child: Text(
|
|
||||||
context.l10n.blacklisted,
|
|
||||||
style: chipTextVariant.copyWith(
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
artist.name!,
|
|
||||||
style: mediaQuery.smAndDown
|
|
||||||
? textTheme.headlineSmall
|
|
||||||
: textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
context.l10n.followers(
|
|
||||||
PrimitiveUtils.toReadableNumber(
|
|
||||||
artist.followers!.total!.toDouble(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: textTheme.bodyMedium?.copyWith(
|
const Gap(20),
|
||||||
fontWeight: mediaQuery.mdAndUp ? FontWeight.bold : null,
|
Flexible(
|
||||||
),
|
child: Column(
|
||||||
),
|
mainAxisSize: MainAxisSize.min,
|
||||||
const Gap(20),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Skeleton.keep(
|
children: [
|
||||||
child: Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (auth.asData?.value != null)
|
OutlineBadge(
|
||||||
Consumer(
|
child:
|
||||||
builder: (context, ref, _) {
|
Text(context.l10n.artist).small().muted(),
|
||||||
final isFollowingQuery = ref
|
),
|
||||||
.watch(artistIsFollowingProvider(artist.id!));
|
if (isBlackListed) ...[
|
||||||
final followingArtistNotifier =
|
const Gap(5),
|
||||||
ref.watch(followedArtistsProvider.notifier);
|
DestructiveBadge(
|
||||||
|
child: Text(context.l10n.blacklisted).small(),
|
||||||
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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
IconButton(
|
|
||||||
tooltip: context.l10n.add_artist_to_blacklist,
|
|
||||||
icon: Icon(
|
|
||||||
SpotubeIcons.userRemove,
|
|
||||||
color:
|
|
||||||
!isBlackListed ? Colors.red[400] : Colors.white,
|
|
||||||
),
|
|
||||||
style: IconButton.styleFrom(
|
|
||||||
backgroundColor:
|
|
||||||
isBlackListed ? Colors.red[400] : null,
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
if (isBlackListed) {
|
|
||||||
await ref
|
|
||||||
.read(blacklistProvider.notifier)
|
|
||||||
.remove(artist.id!);
|
|
||||||
} else {
|
|
||||||
await ref.read(blacklistProvider.notifier).add(
|
|
||||||
BlacklistTableCompanion.insert(
|
|
||||||
name: artist.name!,
|
|
||||||
elementId: artist.id!,
|
|
||||||
elementType: BlacklistedType.artist,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(SpotubeIcons.share),
|
|
||||||
onPressed: () async {
|
|
||||||
if (artist.externalUrls?.spotify != null) {
|
|
||||||
await Clipboard.setData(
|
|
||||||
ClipboardData(
|
|
||||||
text: artist.externalUrls!.spotify!,
|
|
||||||
),
|
),
|
||||||
);
|
]
|
||||||
}
|
],
|
||||||
|
),
|
||||||
if (!context.mounted) return;
|
const Gap(10),
|
||||||
|
Flexible(
|
||||||
scaffoldMessenger.showSnackBar(
|
child: AutoSizeText(
|
||||||
SnackBar(
|
artist.name!,
|
||||||
width: 300,
|
style: constrains.smAndDown
|
||||||
behavior: SnackBarBehavior.floating,
|
? typography.h4
|
||||||
content: Text(
|
: typography.h3,
|
||||||
context.l10n.artist_url_copied,
|
maxLines: 2,
|
||||||
textAlign: TextAlign.center,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
minFontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(5),
|
||||||
|
Flexible(
|
||||||
|
child: AutoSizeText(
|
||||||
|
context.l10n.followers(
|
||||||
|
PrimitiveUtils.toReadableNumber(
|
||||||
|
artist.followers!.total!.toDouble(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
maxLines: 1,
|
||||||
},
|
overflow: TextOverflow.ellipsis,
|
||||||
)
|
minFontSize: 12,
|
||||||
],
|
).muted(),
|
||||||
|
),
|
||||||
|
if (constrains.mdAndUp) ...[
|
||||||
|
const Gap(20),
|
||||||
|
actions,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
),
|
if (constrains.smAndDown) ...[
|
||||||
],
|
const Gap(20),
|
||||||
|
actions,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/fake.dart';
|
import 'package:spotube/collections/fake.dart';
|
||||||
@ -19,7 +19,6 @@ class ArtistPageTopTracks extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
|
||||||
|
|
||||||
final playlist = ref.watch(audioPlayerProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
@ -93,46 +92,46 @@ class ArtistPageTopTracks extends HookConsumerWidget {
|
|||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.l10n.top_tracks,
|
context.l10n.top_tracks,
|
||||||
style: theme.textTheme.headlineSmall,
|
style: theme.typography.h4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!isPlaylistPlaying)
|
if (!isPlaylistPlaying)
|
||||||
IconButton(
|
IconButton.outline(
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
SpotubeIcons.queueAdd,
|
SpotubeIcons.queueAdd,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
playlistNotifier.addTracks(topTracks.toList());
|
playlistNotifier.addTracks(topTracks.toList());
|
||||||
scaffoldMessenger.showSnackBar(
|
showToast(
|
||||||
SnackBar(
|
context: context,
|
||||||
width: 300,
|
location: ToastLocation.topRight,
|
||||||
behavior: SnackBarBehavior.floating,
|
builder: (context, overlay) {
|
||||||
content: Text(
|
return SurfaceCard(
|
||||||
context.l10n.added_to_queue(
|
child: Text(
|
||||||
topTracks.length,
|
context.l10n.added_to_queue(
|
||||||
|
topTracks.length,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
);
|
||||||
),
|
},
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
IconButton(
|
IconButton.primary(
|
||||||
|
shape: ButtonShape.circle,
|
||||||
|
enabled: !isPlaylistPlaying,
|
||||||
icon: Skeleton.keep(
|
icon: Skeleton.keep(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
isPlaylistPlaying ? SpotubeIcons.stop : SpotubeIcons.play,
|
isPlaylistPlaying ? SpotubeIcons.pause : SpotubeIcons.play,
|
||||||
color: Colors.white,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: IconButton.styleFrom(
|
|
||||||
backgroundColor: theme.colorScheme.primary,
|
|
||||||
),
|
|
||||||
onPressed: () => playPlaylist(topTracks.toList()),
|
onPressed: () => playPlaylist(topTracks.toList()),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SliverGap(10),
|
||||||
SliverList.builder(
|
SliverList.builder(
|
||||||
itemCount: topTracks.length,
|
itemCount: topTracks.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
Loading…
Reference in New Issue
Block a user