diff --git a/lib/collections/spotube_icons.dart b/lib/collections/spotube_icons.dart index b98b6785..e0839277 100644 --- a/lib/collections/spotube_icons.dart +++ b/lib/collections/spotube_icons.dart @@ -75,4 +75,5 @@ abstract class SpotubeIcons { static const hoverOn = Icons.back_hand_rounded; static const hoverOff = Icons.back_hand_outlined; static const dragHandle = Icons.drag_indicator; + static const lightning = Icons.flash_on_rounded; } diff --git a/lib/components/shared/track_table/track_tile.dart b/lib/components/shared/track_table/track_tile.dart index 34e95af0..f30088e5 100644 --- a/lib/components/shared/track_table/track_tile.dart +++ b/lib/components/shared/track_table/track_tile.dart @@ -310,35 +310,55 @@ class TrackTile extends HookConsumerWidget { tooltip: "More options", itemBuilder: (context) { return [ - if (!playlistQueueNotifier.isTrackOnQueue(track.value)) + if (!playlistQueueNotifier.isTrackOnQueue(track.value)) ...[ PopupMenuItem( padding: EdgeInsets.zero, onTap: () { playlistQueueNotifier.add([track.value]); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: - Text("Added ${track.value.name} to queue"), + content: Text( + "Added ${track.value.name} to queue", + ), ), ); }, child: const ListTile( leading: Icon(SpotubeIcons.queueAdd), - title: Text("Add to queue"), + title: Text("Add to Queue"), ), - ) - else + ), PopupMenuItem( padding: EdgeInsets.zero, onTap: () { - playlistQueueNotifier.remove([track.value]); + playlistQueueNotifier.playNext([track.value]); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - "Removed ${track.value.name} from queue"), + content: + Text("${track.value.name} will play next"), ), ); }, + child: const ListTile( + leading: Icon(SpotubeIcons.lightning), + title: Text("Play next"), + ), + ), + ] else + PopupMenuItem( + padding: EdgeInsets.zero, + onTap: playlist?.activeTrack.id == track.value.id + ? null + : () { + playlistQueueNotifier.remove([track.value]); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "Removed ${track.value.name} from queue"), + ), + ); + }, + enabled: playlist?.activeTrack.id != track.value.id, child: const ListTile( leading: Icon(SpotubeIcons.queueRemove), title: Text("Remove from queue"), diff --git a/lib/components/shared/track_table/tracks_table_view.dart b/lib/components/shared/track_table/tracks_table_view.dart index 4c27b2b7..a95dcd21 100644 --- a/lib/components/shared/track_table/tracks_table_view.dart +++ b/lib/components/shared/track_table/tracks_table_view.dart @@ -41,6 +41,7 @@ class TracksTableView extends HookConsumerWidget { @override Widget build(context, ref) { final playlist = ref.watch(PlaylistQueueNotifier.provider); + final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier); final downloader = ref.watch(downloaderProvider); TextStyle tableHeadStyle = const TextStyle(fontWeight: FontWeight.bold, fontSize: 16); @@ -162,6 +163,32 @@ class TracksTableView extends HookConsumerWidget { ], ), ), + PopupMenuItem( + enabled: selectedTracks.isNotEmpty, + value: "add-to-queue", + child: Row( + children: [ + const Icon(SpotubeIcons.queueAdd), + const SizedBox(width: 5), + Text( + "Add (${selectedTracks.length}) to Queue", + ), + ], + ), + ), + PopupMenuItem( + enabled: selectedTracks.isNotEmpty, + value: "play-next", + child: Row( + children: [ + const Icon(SpotubeIcons.lightning), + const SizedBox(width: 5), + Text( + "Play (${selectedTracks.length}) Next", + ), + ], + ), + ), ]; }, onSelected: (action) async { @@ -194,6 +221,20 @@ class TracksTableView extends HookConsumerWidget { ); break; } + case "play-next": + { + playlistNotifier.playNext(selectedTracks.toList()); + selected.value = []; + showCheck.value = false; + break; + } + case "add-to-queue": + { + playlistNotifier.add(selectedTracks.toList()); + selected.value = []; + showCheck.value = false; + break; + } default: } }, diff --git a/lib/provider/playlist_queue_provider.dart b/lib/provider/playlist_queue_provider.dart index 0748ff91..c15f10ef 100644 --- a/lib/provider/playlist_queue_provider.dart +++ b/lib/provider/playlist_queue_provider.dart @@ -266,6 +266,18 @@ class PlaylistQueueNotifier extends PersistedStateNotifier { } } + void playNext(List tracks) { + if (!isLoaded) { + loadAndPlay(tracks); + } else { + final stateTracks = state!.tracks.toList(); + + stateTracks.insertAll(state!.active + 1, tracks); + + state = state?.copyWith(tracks: Set.from(stateTracks)); + } + } + void remove(List tracks) { if (!isLoaded) return; final trackIds = tracks.map((e) => e.id!).toSet();