mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
chore: fix queue order
This commit is contained in:
parent
cfda46e07e
commit
0a604a9ad5
@ -15,49 +15,53 @@ import 'package:spotube/provider/history/history.dart';
|
|||||||
import 'package:spotube/provider/audio_player/audio_player.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
|
|
||||||
|
ToastOverlay showToastForAction(
|
||||||
|
BuildContext context,
|
||||||
|
String action,
|
||||||
|
int count,
|
||||||
|
) {
|
||||||
|
final message = switch (action) {
|
||||||
|
"download" => (context.l10n.download_count(count), SpotubeIcons.download),
|
||||||
|
"add-to-playlist" => (
|
||||||
|
context.l10n.add_count_to_playlist(count),
|
||||||
|
SpotubeIcons.playlistAdd
|
||||||
|
),
|
||||||
|
"add-to-queue" => (
|
||||||
|
context.l10n.add_count_to_queue(count),
|
||||||
|
SpotubeIcons.queueAdd
|
||||||
|
),
|
||||||
|
"play-next" => (
|
||||||
|
context.l10n.play_count_next(count),
|
||||||
|
SpotubeIcons.lightning
|
||||||
|
),
|
||||||
|
_ => ("", SpotubeIcons.error),
|
||||||
|
};
|
||||||
|
|
||||||
|
return showToast(
|
||||||
|
context: context,
|
||||||
|
location: ToastLocation.topRight,
|
||||||
|
builder: (context, overlay) {
|
||||||
|
return SurfaceCard(
|
||||||
|
child: Basic(
|
||||||
|
leading: Icon(message.$2),
|
||||||
|
title: Text(message.$1),
|
||||||
|
leadingAlignment: Alignment.center,
|
||||||
|
trailing: IconButton.ghost(
|
||||||
|
size: ButtonSize.small,
|
||||||
|
icon: const Icon(SpotubeIcons.close),
|
||||||
|
onPressed: () {
|
||||||
|
overlay.close();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class TrackPresentationActionsSection extends HookConsumerWidget {
|
class TrackPresentationActionsSection extends HookConsumerWidget {
|
||||||
const TrackPresentationActionsSection({super.key});
|
const TrackPresentationActionsSection({super.key});
|
||||||
|
|
||||||
showToastForAction(BuildContext context, String action, int count) {
|
|
||||||
final message = switch (action) {
|
|
||||||
"download" => (context.l10n.download_count(count), SpotubeIcons.download),
|
|
||||||
"add-to-playlist" => (
|
|
||||||
context.l10n.add_count_to_playlist(count),
|
|
||||||
SpotubeIcons.playlistAdd
|
|
||||||
),
|
|
||||||
"add-to-queue" => (
|
|
||||||
context.l10n.add_count_to_queue(count),
|
|
||||||
SpotubeIcons.queueAdd
|
|
||||||
),
|
|
||||||
"play-next" => (
|
|
||||||
context.l10n.play_count_next(count),
|
|
||||||
SpotubeIcons.lightning
|
|
||||||
),
|
|
||||||
_ => ("", SpotubeIcons.error),
|
|
||||||
};
|
|
||||||
|
|
||||||
showToast(
|
|
||||||
context: context,
|
|
||||||
location: ToastLocation.topRight,
|
|
||||||
builder: (context, overlay) {
|
|
||||||
return SurfaceCard(
|
|
||||||
child: Basic(
|
|
||||||
leading: Icon(message.$2),
|
|
||||||
title: Text(message.$1),
|
|
||||||
leadingAlignment: Alignment.center,
|
|
||||||
trailing: IconButton.ghost(
|
|
||||||
size: ButtonSize.small,
|
|
||||||
icon: const Icon(SpotubeIcons.close),
|
|
||||||
onPressed: () {
|
|
||||||
overlay.close();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final options = TrackPresentationOptions.of(context);
|
final options = TrackPresentationOptions.of(context);
|
||||||
|
@ -30,7 +30,7 @@ class TrackPresentationTopSection extends HookConsumerWidget {
|
|||||||
|
|
||||||
final imageDimension = mediaQuery.mdAndUp ? 200 : 120;
|
final imageDimension = mediaQuery.mdAndUp ? 200 : 120;
|
||||||
|
|
||||||
final (:isLoading, :isActive, :onPlay, :onShuffle) =
|
final (:isLoading, :isActive, :onPlay, :onShuffle, :onAddToQueue) =
|
||||||
useActionCallbacks(ref);
|
useActionCallbacks(ref);
|
||||||
|
|
||||||
final playbackActions = Row(
|
final playbackActions = Row(
|
||||||
@ -59,15 +59,15 @@ class TrackPresentationTopSection extends HookConsumerWidget {
|
|||||||
child: IconButton.secondary(
|
child: IconButton.secondary(
|
||||||
icon: const Icon(SpotubeIcons.queueAdd),
|
icon: const Icon(SpotubeIcons.queueAdd),
|
||||||
enabled: !isLoading && !isActive,
|
enabled: !isLoading && !isActive,
|
||||||
onPressed: () {},
|
onPressed: onAddToQueue,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
Button.secondary(
|
Button.secondary(
|
||||||
leading: const Icon(SpotubeIcons.add),
|
leading: const Icon(SpotubeIcons.add),
|
||||||
enabled: !isLoading && !isActive,
|
enabled: !isLoading && !isActive,
|
||||||
|
onPressed: onAddToQueue,
|
||||||
child: Text(context.l10n.queue),
|
child: Text(context.l10n.queue),
|
||||||
onPressed: () {},
|
|
||||||
),
|
),
|
||||||
Button.primary(
|
Button.primary(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/components/dialogs/select_device_dialog.dart';
|
import 'package:spotube/components/dialogs/select_device_dialog.dart';
|
||||||
|
import 'package:spotube/components/track_presentation/presentation_actions.dart';
|
||||||
import 'package:spotube/components/track_presentation/presentation_props.dart';
|
import 'package:spotube/components/track_presentation/presentation_props.dart';
|
||||||
|
|
||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
@ -17,6 +19,7 @@ typedef UseActionCallbacks = ({
|
|||||||
bool isLoading,
|
bool isLoading,
|
||||||
Future<void> Function() onShuffle,
|
Future<void> Function() onShuffle,
|
||||||
Future<void> Function() onPlay,
|
Future<void> Function() onPlay,
|
||||||
|
VoidCallback onAddToQueue,
|
||||||
});
|
});
|
||||||
|
|
||||||
UseActionCallbacks useActionCallbacks(WidgetRef ref) {
|
UseActionCallbacks useActionCallbacks(WidgetRef ref) {
|
||||||
@ -96,6 +99,7 @@ UseActionCallbacks useActionCallbacks(WidgetRef ref) {
|
|||||||
if (isRemoteDevice == null) return;
|
if (isRemoteDevice == null) return;
|
||||||
if (isRemoteDevice) {
|
if (isRemoteDevice) {
|
||||||
final allTracks = await options.pagination.onFetchAll();
|
final allTracks = await options.pagination.onFetchAll();
|
||||||
|
|
||||||
final remotePlayback = ref.read(connectProvider.notifier);
|
final remotePlayback = ref.read(connectProvider.notifier);
|
||||||
await remotePlayback.load(
|
await remotePlayback.load(
|
||||||
options.collection is SpotubeSimpleAlbumObject
|
options.collection is SpotubeSimpleAlbumObject
|
||||||
@ -109,14 +113,19 @@ UseActionCallbacks useActionCallbacks(WidgetRef ref) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
if (initialTracks.isEmpty) return;
|
||||||
|
|
||||||
await playlistNotifier.load(initialTracks, autoPlay: true);
|
await playlistNotifier.load(initialTracks, autoPlay: true);
|
||||||
playlistNotifier.addCollection(options.collectionId);
|
playlistNotifier.addCollection(options.collectionId);
|
||||||
|
|
||||||
if (options.collection is SpotubeSimpleAlbumObject) {
|
if (options.collection is SpotubeSimpleAlbumObject) {
|
||||||
historyNotifier
|
historyNotifier.addAlbums(
|
||||||
.addAlbums([options.collection as SpotubeSimpleAlbumObject]);
|
[options.collection as SpotubeSimpleAlbumObject],
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
historyNotifier.addPlaylists(
|
historyNotifier.addPlaylists(
|
||||||
[options.collection as SpotubeSimplePlaylistObject]);
|
[options.collection as SpotubeSimplePlaylistObject],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final allTracks = await options.pagination.onFetchAll();
|
final allTracks = await options.pagination.onFetchAll();
|
||||||
@ -132,10 +141,26 @@ UseActionCallbacks useActionCallbacks(WidgetRef ref) {
|
|||||||
}
|
}
|
||||||
}, [options, playlistNotifier, historyNotifier]);
|
}, [options, playlistNotifier, historyNotifier]);
|
||||||
|
|
||||||
|
final onAddToQueue = useCallback(() {
|
||||||
|
final tracks = options.tracks;
|
||||||
|
playlistNotifier.addTracks(tracks);
|
||||||
|
playlistNotifier.addCollection(options.collectionId);
|
||||||
|
if (options.collection is SpotubeSimpleAlbumObject) {
|
||||||
|
historyNotifier
|
||||||
|
.addAlbums([options.collection as SpotubeSimpleAlbumObject]);
|
||||||
|
} else {
|
||||||
|
historyNotifier
|
||||||
|
.addPlaylists([options.collection as SpotubeSimplePlaylistObject]);
|
||||||
|
}
|
||||||
|
if (!context.mounted) return;
|
||||||
|
showToastForAction(context, "add-to-queue", tracks.length);
|
||||||
|
}, [options, playlistNotifier, historyNotifier]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isActive: isActive,
|
isActive: isActive,
|
||||||
isLoading: isLoading.value,
|
isLoading: isLoading.value,
|
||||||
onShuffle: onShuffle,
|
onShuffle: onShuffle,
|
||||||
onPlay: onPlay,
|
onPlay: onPlay,
|
||||||
|
onAddToQueue: onAddToQueue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -144,16 +144,21 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
}),
|
}),
|
||||||
audioPlayer.playlistStream.listen((playlist) async {
|
audioPlayer.playlistStream.listen((playlist) async {
|
||||||
try {
|
try {
|
||||||
|
// Playlist and state has to be in sync. This is only meant for
|
||||||
|
// the shuffle/re-ordering indices to be in sync
|
||||||
|
if (playlist.medias.length != state.tracks.length) return;
|
||||||
|
|
||||||
final queries = playlist.medias
|
final queries = playlist.medias
|
||||||
.map((media) => TrackSourceQuery.parseUri(media.uri))
|
.map((media) => TrackSourceQuery.parseUri(media.uri))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
final trackGroupedById = groupBy(
|
||||||
|
state.tracks,
|
||||||
|
(query) => query.id,
|
||||||
|
);
|
||||||
|
|
||||||
final tracks = queries
|
final tracks = queries
|
||||||
.map(
|
.map((query) => trackGroupedById[query.id]?.firstOrNull)
|
||||||
(query) => state.tracks.firstWhereOrNull(
|
|
||||||
(element) => element.id == query.id,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.nonNulls
|
.nonNulls
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
@ -269,12 +274,12 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
_assertAllowedTracks(tracks);
|
_assertAllowedTracks(tracks);
|
||||||
|
|
||||||
tracks = _blacklist.filter(tracks).toList();
|
tracks = _blacklist.filter(tracks).toList();
|
||||||
for (final track in tracks) {
|
|
||||||
await audioPlayer.addTrack(SpotubeMedia(track));
|
|
||||||
}
|
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
tracks: [...state.tracks, ...tracks],
|
tracks: [...state.tracks, ...tracks],
|
||||||
);
|
);
|
||||||
|
for (final track in tracks) {
|
||||||
|
await audioPlayer.addTrack(SpotubeMedia(track));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeTrack(String trackId) async {
|
Future<void> removeTrack(String trackId) async {
|
||||||
|
Loading…
Reference in New Issue
Block a user