import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../app/router.dart'; import '../../app/theme.dart'; import '../../offline/download_store.dart'; import '../../audio/player_providers.dart'; import '../../core/models.dart'; import '../../data/catalog_repository.dart'; import '../../data/user_library.dart'; import 'artwork.dart'; class TrackTile extends ConsumerWidget { const TrackTile({super.key, required this.track, required this.onTap, this.subtitle, this.card = false}); final Track track; final VoidCallback onTap; final String? subtitle; /// Card style (rounded surface) used on Home; plain rows elsewhere. final bool card; @override Widget build(BuildContext context, WidgetRef ref) { final catalog = ref.watch(catalogProvider).requireValue; final isCurrent = ref.watch(mediaItemProvider).value?.id == track.id; final downloads = ref.watch(downloadsProvider); final text = Theme.of(context).textTheme; final row = InkWell( onTap: onTap, borderRadius: BorderRadius.circular(16), child: Padding( padding: EdgeInsets.symmetric(horizontal: card ? 12 : 0, vertical: card ? 10 : 6), child: Row( children: [ TrackArtwork.track(track, size: card ? 60 : 52, radius: 10), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( track.titleLatin, maxLines: 1, overflow: TextOverflow.ellipsis, style: text.titleMedium?.copyWith(color: isCurrent ? NkColors.sun : NkColors.cream), ), const SizedBox(height: 2), Row( children: [ if (downloads.isDownloaded(track.id)) const Padding( padding: EdgeInsets.only(right: 4), child: Icon(Icons.download_done_rounded, size: 16, color: NkColors.teaLeaf), ) else if (downloads.progress.containsKey(track.id)) const Padding( padding: EdgeInsets.only(right: 6), child: SizedBox.square(dimension: 12, child: CircularProgressIndicator(strokeWidth: 2)), ), Expanded( child: Text( subtitle ?? '${catalog.creditLine(track)} ยท ${formatDuration(track.duration)}', maxLines: 1, overflow: TextOverflow.ellipsis, style: text.bodyMedium?.copyWith(color: NkColors.muted), ), ), ], ), ], ), ), TrackMenuButton(track: track), ], ), ), ); if (!card) return row; return Material(color: NkColors.surface, borderRadius: BorderRadius.circular(16), child: row); } } class TrackMenuButton extends ConsumerWidget { const TrackMenuButton({super.key, required this.track}); final Track track; @override Widget build(BuildContext context, WidgetRef ref) { return IconButton( tooltip: 'More options', icon: const Icon(Icons.more_vert, color: NkColors.muted), onPressed: () => showTrackActions(context, ref, track), ); } } Future showTrackActions(BuildContext context, WidgetRef ref, Track track) { final catalog = ref.read(catalogProvider).requireValue; final controller = ref.read(playerControllerProvider); final liked = ref.read(likedSongsProvider).contains(track.id); final downloaded = ref.read(downloadsProvider).isDownloaded(track.id); final artists = catalog.artistsOf(track); void done(String message) { Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); } return showModalBottomSheet( context: context, backgroundColor: NkColors.surface, showDragHandle: true, builder: (sheetContext) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: TrackArtwork.track(track, size: 48), title: Text(track.titleLatin, style: Theme.of(context).textTheme.titleMedium), subtitle: Text(catalog.creditLine(track)), ), const Divider(color: NkColors.outline), ListTile( leading: const Icon(Icons.playlist_play), title: const Text('Play next'), onTap: () { controller.playNext(track); done('Playing next'); }, ), ListTile( leading: const Icon(Icons.queue_music), title: const Text('Add to queue'), onTap: () { controller.addToQueue(track); done('Added to queue'); }, ), ListTile( leading: Icon(liked ? Icons.favorite : Icons.favorite_border, color: liked ? NkColors.like : null), title: Text(liked ? 'Remove from liked songs' : 'Add to liked songs'), onTap: () { ref.read(likedSongsProvider.notifier).toggle(track.id); done(liked ? 'Removed from liked songs' : 'Added to liked songs'); }, ), ListTile( leading: Icon(downloaded ? Icons.download_done_rounded : Icons.download_rounded, color: downloaded ? NkColors.teaLeaf : null), title: Text(downloaded ? 'Remove download' : 'Download'), onTap: () { if (downloaded) { ref.read(downloadStoreProvider).remove(track.id); done('Download removed'); } else if (!isSignedIn) { Navigator.of(sheetContext).pop(); context.push('/login?mode=signup'); } else { controller.download([track]).then((problem) => done(problem ?? 'Downloading. Find it in Downloads.')); } }, ), ListTile( leading: const Icon(Icons.flag_outlined), title: const Text('Is this your song? Report'), onTap: () { Navigator.of(sheetContext).pop(); context.push('/report?track=${track.id}'); }, ), for (final a in artists) ListTile( leading: const Icon(Icons.person_outline), title: Text('Go to ${a.name}'), onTap: () { Navigator.of(sheetContext).pop(); context.push('/artist/${a.id}'); }, ), ], ), ), ); }