mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
241 lines
9.9 KiB
Dart
241 lines
9.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:gap/gap.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:skeletonizer/skeletonizer.dart';
|
|
import 'package:spotube/collections/fake.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/extensions/context.dart';
|
|
import 'package:spotube/extensions/image.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/spotify.dart';
|
|
import 'package:spotube/utils/primitive_utils.dart';
|
|
|
|
class ArtistPageHeader extends HookConsumerWidget {
|
|
final String artistId;
|
|
const ArtistPageHeader({super.key, required this.artistId});
|
|
|
|
@override
|
|
Widget build(BuildContext context, ref) {
|
|
final artistQuery = ref.watch(artistProvider(artistId));
|
|
final artist = artistQuery.asData?.value ?? FakeData.artist;
|
|
|
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
|
final mediaQuery = MediaQuery.of(context);
|
|
final theme = Theme.of(context);
|
|
final ThemeData(:textTheme) = 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(AuthenticationNotifier.provider);
|
|
final blacklist = ref.watch(BlackListNotifier.provider);
|
|
final isBlackListed = blacklist.contains(
|
|
BlacklistedElement.artist(artistId, artist.name!),
|
|
);
|
|
|
|
final image = artist.images.asUrlString(
|
|
placeholder: ImagePlaceholder.artist,
|
|
);
|
|
|
|
return LayoutBuilder(
|
|
builder: (context, constrains) {
|
|
return Center(
|
|
child: Flex(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: constrains.smAndDown
|
|
? CrossAxisAlignment.start
|
|
: CrossAxisAlignment.center,
|
|
direction: constrains.smAndDown ? Axis.vertical : Axis.horizontal,
|
|
children: [
|
|
DecoratedBox(
|
|
decoration: BoxDecoration(
|
|
boxShadow: kElevationToShadow[2],
|
|
borderRadius: BorderRadius.circular(35),
|
|
),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(35),
|
|
child: UniversalImage(
|
|
path: image,
|
|
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(
|
|
fontWeight: mediaQuery.mdAndUp ? FontWeight.bold : null,
|
|
),
|
|
),
|
|
const Gap(20),
|
|
Skeleton.keep(
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (auth != 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 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) {
|
|
ref
|
|
.read(BlackListNotifier.provider.notifier)
|
|
.remove(
|
|
BlacklistedElement.artist(
|
|
artist.id!, artist.name!),
|
|
);
|
|
} else {
|
|
ref.read(BlackListNotifier.provider.notifier).add(
|
|
BlacklistedElement.artist(
|
|
artist.id!, artist.name!),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
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;
|
|
|
|
scaffoldMessenger.showSnackBar(
|
|
SnackBar(
|
|
width: 300,
|
|
behavior: SnackBarBehavior.floating,
|
|
content: Text(
|
|
context.l10n.artist_url_copied,
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
)
|
|
],
|
|
),
|
|
)
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|