spotube/lib/components/track_tile/track_options.dart
2025-08-29 17:33:54 +06:00

281 lines
9.0 KiB
Dart

import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/ui/button_tile.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/track_options/track_options_provider.dart';
/// [track] must be a [SpotubeFullTrackObject] or [SpotubeLocalTrackObject]
class TrackOptions extends HookConsumerWidget {
final SpotubeTrackObject track;
final bool userPlaylist;
final String? playlistId;
final Widget? icon;
const TrackOptions({
super.key,
required this.track,
this.userPlaylist = false,
this.playlistId,
this.icon,
}) : assert(
track is SpotubeFullTrackObject || track is SpotubeLocalTrackObject,
"Track must be a SpotubeFullTrackObject, SpotubeLocalTrackObject",
);
@override
Widget build(BuildContext context, ref) {
final mediaQuery = MediaQuery.of(context);
final ThemeData(:colorScheme) = Theme.of(context);
final trackOptionActions = ref.watch(trackOptionActionsProvider(track));
final (
:isBlacklisted,
:isInDownloadQueue,
:isInQueue,
:isActiveTrack,
:isAuthenticated,
:isLiked,
:progressNotifier
) = ref.watch(trackOptionsStateProvider(track));
final isLocalTrack = track is SpotubeLocalTrackObject;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: [
if (isLocalTrack)
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.delete,
playlistId,
);
},
leading: const Icon(SpotubeIcons.trash),
title: Text(context.l10n.delete),
),
if (mediaQuery.smAndDown && !isLocalTrack)
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.album,
playlistId,
);
},
leading: const Icon(SpotubeIcons.album),
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(context.l10n.go_to_album),
Text(
track.album.name,
style: context.theme.typography.xSmall,
),
],
),
),
if (!isInQueue) ...[
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.addToQueue,
playlistId,
);
},
leading: const Icon(SpotubeIcons.queueAdd),
title: Text(context.l10n.add_to_queue),
),
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.playNext,
playlistId,
);
},
leading: const Icon(SpotubeIcons.lightning),
title: Text(context.l10n.play_next),
),
] else
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.removeFromQueue,
playlistId,
);
},
enabled: !isActiveTrack,
leading: const Icon(SpotubeIcons.queueRemove),
title: Text(context.l10n.remove_from_queue),
),
if (isAuthenticated && !isLocalTrack)
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.favorite,
playlistId,
);
},
leading: isLiked
? const Icon(
SpotubeIcons.heartFilled,
color: Colors.pink,
)
: const Icon(SpotubeIcons.heart),
title: Text(
isLiked
? context.l10n.remove_from_favorites
: context.l10n.save_as_favorite,
),
),
if (isAuthenticated && !isLocalTrack) ...[
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.startRadio,
playlistId,
);
},
leading: const Icon(SpotubeIcons.radio),
title: Text(context.l10n.start_a_radio),
),
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.addToPlaylist,
playlistId,
);
},
leading: const Icon(SpotubeIcons.playlistAdd),
title: Text(context.l10n.add_to_playlist),
),
],
if (userPlaylist && isAuthenticated && !isLocalTrack)
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.removeFromPlaylist,
playlistId,
);
},
leading: const Icon(SpotubeIcons.removeFilled),
title: Text(context.l10n.remove_from_playlist),
),
if (!isLocalTrack)
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.download,
playlistId,
);
},
enabled: !isInDownloadQueue,
leading: isInDownloadQueue
? HookBuilder(builder: (context) {
final progress = useListenable(progressNotifier);
return CircularProgressIndicator(
value: progress?.value,
);
})
: const Icon(SpotubeIcons.download),
title: Text(context.l10n.download_track),
),
if (!isLocalTrack)
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.blacklist,
playlistId,
);
},
leading: Icon(
SpotubeIcons.playlistRemove,
color: isBlacklisted != true ? Colors.red[400] : null,
),
title: Text(
isBlacklisted == true
? context.l10n.remove_from_blacklist
: context.l10n.add_to_blacklist,
style: TextStyle(
color: isBlacklisted != true ? Colors.red[400] : null,
),
),
),
if (!isLocalTrack)
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.share,
playlistId,
);
},
leading: const Icon(SpotubeIcons.share),
title: Text(context.l10n.share),
),
if (!isLocalTrack)
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.songlink,
playlistId,
);
},
leading: Assets.images.logos.songlinkTransparent.image(
width: 22,
height: 22,
color: colorScheme.foreground.withValues(alpha: 0.5),
),
title: Text(context.l10n.song_link),
),
if (!isLocalTrack)
ButtonTile(
style: ButtonVariance.menu,
onPressed: () async {
await trackOptionActions.action(
context,
TrackOptionValue.details,
playlistId,
);
},
leading: const Icon(SpotubeIcons.info),
title: Text(context.l10n.details),
),
],
);
}
}