mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
refactor: replace PopupMenuButton widgets with AdaptivePopSheetList
This commit is contained in:
parent
ddc1c5f373
commit
3b56c78d5c
@ -42,7 +42,7 @@ class AdaptivePopSheetList<T> extends StatelessWidget {
|
|||||||
this.borderRadius = const BorderRadius.all(Radius.circular(999)),
|
this.borderRadius = const BorderRadius.all(Radius.circular(999)),
|
||||||
this.tooltip,
|
this.tooltip,
|
||||||
}) : assert(
|
}) : assert(
|
||||||
icon != null || child != null,
|
!(icon != null && child != null),
|
||||||
'Either icon or child must be provided',
|
'Either icon or child must be provided',
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -55,21 +55,14 @@ class AdaptivePopSheetList<T> extends StatelessWidget {
|
|||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
icon: icon,
|
icon: icon,
|
||||||
tooltip: tooltip,
|
tooltip: tooltip,
|
||||||
child: IgnorePointer(child: child),
|
child: child == null ? null : IgnorePointer(child: child),
|
||||||
itemBuilder: (context) => children
|
itemBuilder: (context) => children
|
||||||
.map(
|
.map(
|
||||||
(item) => PopupMenuItem(
|
(item) => PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
child: ListTile(
|
child: _AdaptivePopSheetListItem(
|
||||||
enabled: item.enabled,
|
item: item,
|
||||||
onTap: () {
|
onSelected: onSelected,
|
||||||
item.onTap?.call();
|
|
||||||
Navigator.pop(context);
|
|
||||||
if (item.value != null) {
|
|
||||||
onSelected?.call(item.value as T);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: item.child,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -90,7 +83,17 @@ class AdaptivePopSheetList<T> extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (headings != null) ...[
|
if (headings != null) ...[
|
||||||
|
Container(
|
||||||
|
width: 180,
|
||||||
|
height: 6,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
borderRadius: BorderRadius.circular(999),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
...headings!,
|
...headings!,
|
||||||
|
const SizedBox(height: 8),
|
||||||
Divider(
|
Divider(
|
||||||
color: theme.colorScheme.primary,
|
color: theme.colorScheme.primary,
|
||||||
thickness: 0.3,
|
thickness: 0.3,
|
||||||
@ -99,16 +102,9 @@ class AdaptivePopSheetList<T> extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
...children.map(
|
...children.map(
|
||||||
(item) => ListTile(
|
(item) => _AdaptivePopSheetListItem(
|
||||||
onTap: () {
|
item: item,
|
||||||
item.onTap?.call();
|
onSelected: onSelected,
|
||||||
Navigator.pop(context);
|
|
||||||
if (item.value != null) {
|
|
||||||
onSelected?.call(item.value as T);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
enabled: item.enabled,
|
|
||||||
title: item.child,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -144,3 +140,41 @@ class AdaptivePopSheetList<T> extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _AdaptivePopSheetListItem<T> extends StatelessWidget {
|
||||||
|
final PopSheetEntry<T> item;
|
||||||
|
final ValueChanged<T>? onSelected;
|
||||||
|
const _AdaptivePopSheetListItem({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
this.onSelected,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
return InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
onTap: !item.enabled
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
item.onTap?.call();
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (item.value != null) {
|
||||||
|
onSelected?.call(item.value as T);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style: TextStyle(
|
||||||
|
color: item.enabled
|
||||||
|
? theme.textTheme.bodyMedium!.color
|
||||||
|
: theme.textTheme.bodyMedium!.color!.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
child: item.child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -25,32 +25,50 @@ class SortTracksDropdown extends StatelessWidget {
|
|||||||
PopSheetEntry(
|
PopSheetEntry(
|
||||||
value: SortBy.none,
|
value: SortBy.none,
|
||||||
enabled: value != SortBy.none,
|
enabled: value != SortBy.none,
|
||||||
child: Text(context.l10n.none),
|
child: ListTile(
|
||||||
|
enabled: value != SortBy.none,
|
||||||
|
title: Text(context.l10n.none),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PopSheetEntry(
|
PopSheetEntry(
|
||||||
value: SortBy.ascending,
|
value: SortBy.ascending,
|
||||||
enabled: value != SortBy.ascending,
|
enabled: value != SortBy.ascending,
|
||||||
child: Text(context.l10n.sort_a_z),
|
child: ListTile(
|
||||||
|
enabled: value != SortBy.ascending,
|
||||||
|
title: Text(context.l10n.sort_a_z),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PopSheetEntry(
|
PopSheetEntry(
|
||||||
value: SortBy.descending,
|
value: SortBy.descending,
|
||||||
enabled: value != SortBy.descending,
|
enabled: value != SortBy.descending,
|
||||||
child: Text(context.l10n.sort_z_a),
|
child: ListTile(
|
||||||
|
enabled: value != SortBy.descending,
|
||||||
|
title: Text(context.l10n.sort_z_a),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PopSheetEntry(
|
PopSheetEntry(
|
||||||
value: SortBy.dateAdded,
|
value: SortBy.dateAdded,
|
||||||
enabled: value != SortBy.dateAdded,
|
enabled: value != SortBy.dateAdded,
|
||||||
child: Text(context.l10n.sort_date),
|
child: ListTile(
|
||||||
|
enabled: value != SortBy.dateAdded,
|
||||||
|
title: Text(context.l10n.sort_date),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PopSheetEntry(
|
PopSheetEntry(
|
||||||
value: SortBy.artist,
|
value: SortBy.artist,
|
||||||
enabled: value != SortBy.artist,
|
enabled: value != SortBy.artist,
|
||||||
child: Text(context.l10n.sort_artist),
|
child: ListTile(
|
||||||
|
enabled: value != SortBy.artist,
|
||||||
|
title: Text(context.l10n.sort_artist),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PopSheetEntry(
|
PopSheetEntry(
|
||||||
value: SortBy.album,
|
value: SortBy.album,
|
||||||
enabled: value != SortBy.album,
|
enabled: value != SortBy.album,
|
||||||
child: Text(context.l10n.sort_album),
|
child: ListTile(
|
||||||
|
enabled: value != SortBy.album,
|
||||||
|
title: Text(context.l10n.sort_album),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
headings: [
|
headings: [
|
||||||
|
@ -7,15 +7,29 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/library/user_local_tracks.dart';
|
import 'package:spotube/components/library/user_local_tracks.dart';
|
||||||
|
import 'package:spotube/components/shared/adaptive/adaptive_pop_sheet_list.dart';
|
||||||
import 'package:spotube/components/shared/dialogs/playlist_add_track_dialog.dart';
|
import 'package:spotube/components/shared/dialogs/playlist_add_track_dialog.dart';
|
||||||
import 'package:spotube/components/shared/heart_button.dart';
|
import 'package:spotube/components/shared/heart_button.dart';
|
||||||
import 'package:spotube/extensions/constrains.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
import 'package:spotube/provider/authentication_provider.dart';
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
import 'package:spotube/provider/blacklist_provider.dart';
|
import 'package:spotube/provider/blacklist_provider.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||||
import 'package:spotube/services/mutations/mutations.dart';
|
import 'package:spotube/services/mutations/mutations.dart';
|
||||||
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
|
|
||||||
|
enum TrackOptionValue {
|
||||||
|
share,
|
||||||
|
addToPlaylist,
|
||||||
|
addToQueue,
|
||||||
|
removeFromPlaylist,
|
||||||
|
removeFromQueue,
|
||||||
|
blacklist,
|
||||||
|
delete,
|
||||||
|
playNext,
|
||||||
|
favorite,
|
||||||
|
}
|
||||||
|
|
||||||
class TrackOptions extends HookConsumerWidget {
|
class TrackOptions extends HookConsumerWidget {
|
||||||
final Track track;
|
final Track track;
|
||||||
@ -80,151 +94,62 @@ class TrackOptions extends HookConsumerWidget {
|
|||||||
playlistId ?? "",
|
playlistId ?? "",
|
||||||
);
|
);
|
||||||
|
|
||||||
final mediaQuery = MediaQuery.of(context);
|
return ListTileTheme(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
final createItems = useCallback(
|
borderRadius: BorderRadius.circular(10),
|
||||||
(BuildContext context) {
|
),
|
||||||
if (track is LocalTrack) {
|
child: AdaptivePopSheetList<TrackOptionValue>(
|
||||||
return [
|
onSelected: (value) async {
|
||||||
if (mediaQuery.isSm) ...[
|
switch (value) {
|
||||||
Text(
|
case TrackOptionValue.delete:
|
||||||
track.name!,
|
await File((track as LocalTrack).path).delete();
|
||||||
maxLines: 1,
|
ref.refresh(localTracksProvider);
|
||||||
overflow: TextOverflow.ellipsis,
|
break;
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
case TrackOptionValue.addToQueue:
|
||||||
),
|
await playback.addTrack(track);
|
||||||
Divider(
|
if (context.mounted) {
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
thickness: 0.2,
|
|
||||||
indent: 16,
|
|
||||||
endIndent: 16,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
ListTile(
|
|
||||||
onTap: () async {
|
|
||||||
await File((track as LocalTrack).path).delete();
|
|
||||||
ref.refresh(localTracksProvider);
|
|
||||||
if (context.mounted) Navigator.pop(context);
|
|
||||||
},
|
|
||||||
leading: const Icon(SpotubeIcons.trash),
|
|
||||||
title: Text(context.l10n.delete),
|
|
||||||
)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
if (mediaQuery.isSm) ...[
|
|
||||||
Text(
|
|
||||||
track.name!,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
Divider(
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
thickness: 0.2,
|
|
||||||
indent: 16,
|
|
||||||
endIndent: 16,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
if (!playlist.containsTrack(track)) ...[
|
|
||||||
ListTile(
|
|
||||||
onTap: () async {
|
|
||||||
await playback.addTrack(track);
|
|
||||||
if (context.mounted) {
|
|
||||||
scaffoldMessenger.showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
context.l10n.added_track_to_queue(track.name!),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
leading: const Icon(SpotubeIcons.queueAdd),
|
|
||||||
title: Text(context.l10n.add_to_queue),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
onTap: () {
|
|
||||||
playback.addTracksAtFirst([track]);
|
|
||||||
scaffoldMessenger.showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
context.l10n.track_will_play_next(track.name!),
|
context.l10n.added_track_to_queue(track.name!),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Navigator.pop(context);
|
}
|
||||||
},
|
break;
|
||||||
leading: const Icon(SpotubeIcons.lightning),
|
case TrackOptionValue.playNext:
|
||||||
title: Text(context.l10n.play_next),
|
playback.addTracksAtFirst([track]);
|
||||||
),
|
scaffoldMessenger.showSnackBar(
|
||||||
] else
|
SnackBar(
|
||||||
ListTile(
|
content: Text(
|
||||||
onTap: playlist.activeTrack?.id == track.id
|
context.l10n.track_will_play_next(track.name!),
|
||||||
? null
|
),
|
||||||
: () {
|
),
|
||||||
playback.removeTrack(track.id!);
|
);
|
||||||
scaffoldMessenger.showSnackBar(
|
break;
|
||||||
SnackBar(
|
case TrackOptionValue.removeFromQueue:
|
||||||
content: Text(
|
playback.removeTrack(track.id!);
|
||||||
context.l10n.removed_track_from_queue(
|
scaffoldMessenger.showSnackBar(
|
||||||
track.name!,
|
SnackBar(
|
||||||
),
|
content: Text(
|
||||||
),
|
context.l10n.removed_track_from_queue(
|
||||||
),
|
track.name!,
|
||||||
);
|
),
|
||||||
Navigator.pop(context);
|
),
|
||||||
},
|
),
|
||||||
enabled: playlist.activeTrack?.id != track.id,
|
);
|
||||||
leading: const Icon(SpotubeIcons.queueRemove),
|
break;
|
||||||
title: Text(context.l10n.remove_from_queue),
|
case TrackOptionValue.favorite:
|
||||||
),
|
favorites.toggleTrackLike.mutate(favorites.isLiked);
|
||||||
if (favorites.me.hasData)
|
break;
|
||||||
ListTile(
|
case TrackOptionValue.addToPlaylist:
|
||||||
onTap: () {
|
actionAddToPlaylist(context, track);
|
||||||
favorites.toggleTrackLike.mutate(favorites.isLiked);
|
break;
|
||||||
Navigator.pop(context);
|
case TrackOptionValue.removeFromPlaylist:
|
||||||
},
|
removingTrack.value = track.uri;
|
||||||
leading: favorites.isLiked
|
removeTrack.mutate(track.uri!);
|
||||||
? const Icon(
|
break;
|
||||||
SpotubeIcons.heartFilled,
|
case TrackOptionValue.blacklist:
|
||||||
color: Colors.pink,
|
|
||||||
)
|
|
||||||
: const Icon(SpotubeIcons.heart),
|
|
||||||
title: Text(
|
|
||||||
favorites.isLiked
|
|
||||||
? context.l10n.remove_from_favorites
|
|
||||||
: context.l10n.save_as_favorite,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (auth != null)
|
|
||||||
ListTile(
|
|
||||||
onTap: () {
|
|
||||||
actionAddToPlaylist(context, track);
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
leading: const Icon(SpotubeIcons.playlistAdd),
|
|
||||||
title: Text(context.l10n.add_to_playlist),
|
|
||||||
),
|
|
||||||
if (userPlaylist && auth != null)
|
|
||||||
ListTile(
|
|
||||||
onTap: () {
|
|
||||||
removingTrack.value = track.uri;
|
|
||||||
removeTrack.mutate(track.uri!);
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
leading: (removeTrack.isMutating || !removeTrack.hasData) &&
|
|
||||||
removingTrack.value == track.uri
|
|
||||||
? const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
)
|
|
||||||
: const Icon(SpotubeIcons.removeFilled),
|
|
||||||
title: Text(context.l10n.remove_from_playlist),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
onTap: () {
|
|
||||||
if (isBlackListed) {
|
if (isBlackListed) {
|
||||||
ref.read(BlackListNotifier.provider.notifier).remove(
|
ref.read(BlackListNotifier.provider.notifier).remove(
|
||||||
BlacklistedElement.track(track.id!, track.name!),
|
BlacklistedElement.track(track.id!, track.name!),
|
||||||
@ -234,69 +159,139 @@ class TrackOptions extends HookConsumerWidget {
|
|||||||
BlacklistedElement.track(track.id!, track.name!),
|
BlacklistedElement.track(track.id!, track.name!),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Navigator.pop(context);
|
break;
|
||||||
},
|
case TrackOptionValue.share:
|
||||||
leading: const Icon(SpotubeIcons.playlistRemove),
|
actionShare(context, track);
|
||||||
iconColor: !isBlackListed ? Colors.red[400] : null,
|
break;
|
||||||
textColor: !isBlackListed ? Colors.red[400] : null,
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(SpotubeIcons.moreHorizontal),
|
||||||
|
headings: [
|
||||||
|
ListTile(
|
||||||
|
dense: true,
|
||||||
|
leading: AspectRatio(
|
||||||
|
aspectRatio: 1,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: UniversalImage(
|
||||||
|
path: TypeConversionUtils.image_X_UrlString(
|
||||||
|
track.album!.images,
|
||||||
|
placeholder: ImagePlaceholder.albumArt),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
isBlackListed
|
track.name!,
|
||||||
? context.l10n.remove_from_blacklist
|
maxLines: 1,
|
||||||
: context.l10n.add_to_blacklist,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
subtitle: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TypeConversionUtils.artists_X_ClickableArtists(
|
||||||
|
track.artists!,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
],
|
||||||
onTap: () {
|
children: switch (track.runtimeType) {
|
||||||
actionShare(context, track);
|
LocalTrack => [
|
||||||
Navigator.pop(context);
|
PopSheetEntry(
|
||||||
},
|
value: TrackOptionValue.delete,
|
||||||
leading: const Icon(SpotubeIcons.share),
|
child: ListTile(
|
||||||
title: Text(context.l10n.share),
|
leading: const Icon(SpotubeIcons.trash),
|
||||||
)
|
title: Text(context.l10n.delete),
|
||||||
];
|
|
||||||
},
|
|
||||||
[track, playlist, favorites, auth, isBlackListed, mediaQuery],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (mediaQuery.isSm) {
|
|
||||||
return IconButton(
|
|
||||||
icon: const Icon(SpotubeIcons.moreHorizontal),
|
|
||||||
onPressed: () {
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => Padding(
|
|
||||||
padding: const EdgeInsets.all(10.0),
|
|
||||||
child: ListTileTheme(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
),
|
||||||
horizontalTitleGap: 5,
|
)
|
||||||
child: Column(
|
],
|
||||||
mainAxisSize: MainAxisSize.min,
|
_ => [
|
||||||
children: createItems(context),
|
if (!playlist.containsTrack(track)) ...[
|
||||||
|
PopSheetEntry(
|
||||||
|
value: TrackOptionValue.addToQueue,
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.queueAdd),
|
||||||
|
title: Text(context.l10n.add_to_queue),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopSheetEntry(
|
||||||
|
value: TrackOptionValue.playNext,
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.lightning),
|
||||||
|
title: Text(context.l10n.play_next),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
] else
|
||||||
|
PopSheetEntry(
|
||||||
|
value: TrackOptionValue.removeFromQueue,
|
||||||
|
enabled: playlist.activeTrack?.id != track.id,
|
||||||
|
child: ListTile(
|
||||||
|
enabled: playlist.activeTrack?.id != track.id,
|
||||||
|
leading: const Icon(SpotubeIcons.queueRemove),
|
||||||
|
title: Text(context.l10n.remove_from_queue),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (favorites.me.hasData)
|
||||||
|
PopSheetEntry(
|
||||||
|
value: TrackOptionValue.favorite,
|
||||||
|
child: ListTile(
|
||||||
|
leading: favorites.isLiked
|
||||||
|
? const Icon(
|
||||||
|
SpotubeIcons.heartFilled,
|
||||||
|
color: Colors.pink,
|
||||||
|
)
|
||||||
|
: const Icon(SpotubeIcons.heart),
|
||||||
|
title: Text(
|
||||||
|
favorites.isLiked
|
||||||
|
? context.l10n.remove_from_favorites
|
||||||
|
: context.l10n.save_as_favorite,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (auth != null)
|
||||||
|
PopSheetEntry(
|
||||||
|
value: TrackOptionValue.addToPlaylist,
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.playlistAdd),
|
||||||
|
title: Text(context.l10n.add_to_playlist),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (userPlaylist && auth != null)
|
||||||
|
PopSheetEntry(
|
||||||
|
value: TrackOptionValue.removeFromPlaylist,
|
||||||
|
child: ListTile(
|
||||||
|
leading: (removeTrack.isMutating || !removeTrack.hasData) &&
|
||||||
|
removingTrack.value == track.uri
|
||||||
|
? const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
)
|
||||||
|
: const Icon(SpotubeIcons.removeFilled),
|
||||||
|
title: Text(context.l10n.remove_from_playlist),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopSheetEntry(
|
||||||
|
value: TrackOptionValue.blacklist,
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.playlistRemove),
|
||||||
|
iconColor: !isBlackListed ? Colors.red[400] : null,
|
||||||
|
textColor: !isBlackListed ? Colors.red[400] : null,
|
||||||
|
title: Text(
|
||||||
|
isBlackListed
|
||||||
|
? context.l10n.remove_from_blacklist
|
||||||
|
: context.l10n.add_to_blacklist,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
PopSheetEntry(
|
||||||
useRootNavigator: true,
|
value: TrackOptionValue.share,
|
||||||
);
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.share),
|
||||||
|
title: Text(context.l10n.share),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
},
|
},
|
||||||
);
|
),
|
||||||
}
|
|
||||||
|
|
||||||
return PopupMenuButton(
|
|
||||||
icon: const Icon(SpotubeIcons.moreHorizontal),
|
|
||||||
position: PopupMenuPosition.under,
|
|
||||||
tooltip: context.l10n.more_actions,
|
|
||||||
itemBuilder: (context) {
|
|
||||||
return createItems(context)
|
|
||||||
.map(
|
|
||||||
(e) => PopupMenuItem(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
child: e,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
|
import 'package:spotube/components/shared/adaptive/adaptive_pop_sheet_list.dart';
|
||||||
import 'package:spotube/components/shared/dialogs/confirm_download_dialog.dart';
|
import 'package:spotube/components/shared/dialogs/confirm_download_dialog.dart';
|
||||||
import 'package:spotube/components/shared/dialogs/playlist_add_track_dialog.dart';
|
import 'package:spotube/components/shared/dialogs/playlist_add_track_dialog.dart';
|
||||||
import 'package:spotube/components/shared/fallbacks/not_found.dart';
|
import 'package:spotube/components/shared/fallbacks/not_found.dart';
|
||||||
@ -138,70 +139,63 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
.state = value;
|
.state = value;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
PopupMenuButton(
|
AdaptivePopSheetList(
|
||||||
tooltip: context.l10n.more_actions,
|
tooltip: context.l10n.more_actions,
|
||||||
itemBuilder: (context) {
|
headings: [
|
||||||
return [
|
Text(
|
||||||
PopupMenuItem(
|
context.l10n.more_actions,
|
||||||
|
style: tableHeadStyle,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
children: [
|
||||||
|
PopSheetEntry(
|
||||||
|
enabled: selectedTracks.isNotEmpty,
|
||||||
|
value: "download",
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.download),
|
||||||
enabled: selectedTracks.isNotEmpty,
|
enabled: selectedTracks.isNotEmpty,
|
||||||
value: "download",
|
title: Text(
|
||||||
child: Row(
|
context.l10n.download_count(selectedTracks.length),
|
||||||
children: [
|
|
||||||
const Icon(SpotubeIcons.download),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
Text(
|
|
||||||
context.l10n
|
|
||||||
.download_count(selectedTracks.length),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!userPlaylist)
|
),
|
||||||
PopupMenuItem(
|
if (!userPlaylist)
|
||||||
|
PopSheetEntry(
|
||||||
|
enabled: selectedTracks.isNotEmpty,
|
||||||
|
value: "add-to-playlist",
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.playlistAdd),
|
||||||
enabled: selectedTracks.isNotEmpty,
|
enabled: selectedTracks.isNotEmpty,
|
||||||
value: "add-to-playlist",
|
title: Text(
|
||||||
child: Row(
|
context.l10n
|
||||||
children: [
|
.add_count_to_playlist(selectedTracks.length),
|
||||||
const Icon(SpotubeIcons.playlistAdd),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
Text(
|
|
||||||
context.l10n.add_count_to_playlist(
|
|
||||||
selectedTracks.length,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
),
|
||||||
|
PopSheetEntry(
|
||||||
|
enabled: selectedTracks.isNotEmpty,
|
||||||
|
value: "add-to-queue",
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.queueAdd),
|
||||||
enabled: selectedTracks.isNotEmpty,
|
enabled: selectedTracks.isNotEmpty,
|
||||||
value: "add-to-queue",
|
title: Text(
|
||||||
child: Row(
|
context.l10n
|
||||||
children: [
|
.add_count_to_queue(selectedTracks.length),
|
||||||
const Icon(SpotubeIcons.queueAdd),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
Text(
|
|
||||||
context.l10n
|
|
||||||
.add_count_to_queue(selectedTracks.length),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
),
|
||||||
|
PopSheetEntry(
|
||||||
|
enabled: selectedTracks.isNotEmpty,
|
||||||
|
value: "play-next",
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.lightning),
|
||||||
enabled: selectedTracks.isNotEmpty,
|
enabled: selectedTracks.isNotEmpty,
|
||||||
value: "play-next",
|
title: Text(
|
||||||
child: Row(
|
context.l10n.play_count_next(selectedTracks.length),
|
||||||
children: [
|
|
||||||
const Icon(SpotubeIcons.lightning),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
Text(
|
|
||||||
context.l10n
|
|
||||||
.play_count_next(selectedTracks.length),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
),
|
||||||
},
|
],
|
||||||
onSelected: (action) async {
|
onSelected: (action) async {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "download":
|
case "download":
|
||||||
|
Loading…
Reference in New Issue
Block a user