mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-02 09:08:35 +02:00
fix(download): swap local track variant actions
This commit is contained in:
@@ -433,6 +433,7 @@ extension _ArtistScreenSections on _ArtistScreenState {
|
||||
context,
|
||||
ref,
|
||||
track,
|
||||
hasLocalPlaybackCandidate: isInHistory || isInLocalLibrary,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
@@ -527,7 +528,10 @@ extension _ArtistScreenSections on _ArtistScreenState {
|
||||
],
|
||||
),
|
||||
),
|
||||
TrackCollectionQuickActions(track: track),
|
||||
TrackCollectionQuickActions(
|
||||
track: track,
|
||||
hasLocalPlaybackCandidate: isInHistory || isInLocalLibrary,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -264,6 +264,7 @@ class _TrackItemWithStatus extends ConsumerWidget {
|
||||
context,
|
||||
ref,
|
||||
track,
|
||||
hasLocalPlaybackCandidate: isInHistory || isInLocalLibrary,
|
||||
),
|
||||
splashColor: colorScheme.primary.withValues(alpha: 0.12),
|
||||
highlightColor: colorScheme.primary.withValues(alpha: 0.08),
|
||||
@@ -336,7 +337,10 @@ class _TrackItemWithStatus extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
PreviewButton(track: track),
|
||||
TrackCollectionQuickActions(track: track),
|
||||
TrackCollectionQuickActions(
|
||||
track: track,
|
||||
hasLocalPlaybackCandidate: isInHistory || isInLocalLibrary,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
enum QualityVariantMenuAction { downloadAnotherQuality, playLocal }
|
||||
|
||||
/// Resolves the quality-variant entry shown in a remote track's options menu.
|
||||
///
|
||||
/// When tapping an already-local track is repurposed to download another
|
||||
/// quality, the menu becomes the escape hatch for playing that local copy.
|
||||
QualityVariantMenuAction? resolveQualityVariantMenuAction({
|
||||
required bool allowQualityVariants,
|
||||
required bool hasLocalPlaybackCandidate,
|
||||
}) {
|
||||
if (!allowQualityVariants) return null;
|
||||
return hasLocalPlaybackCandidate
|
||||
? QualityVariantMenuAction.playLocal
|
||||
: QualityVariantMenuAction.downloadAnotherQuality;
|
||||
}
|
||||
@@ -6,20 +6,28 @@ import 'package:spotiflac_android/models/track.dart';
|
||||
import 'package:spotiflac_android/providers/library_collections_provider.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/services/cover_cache_manager.dart';
|
||||
import 'package:spotiflac_android/utils/local_playback.dart';
|
||||
import 'package:spotiflac_android/widgets/playlist_picker_sheet.dart';
|
||||
import 'package:spotiflac_android/widgets/track_collection_action_policy.dart';
|
||||
import 'package:spotiflac_android/widgets/track_detail_actions.dart';
|
||||
import 'package:spotiflac_android/utils/clickable_metadata.dart';
|
||||
|
||||
class TrackCollectionQuickActions extends ConsumerWidget {
|
||||
final Track track;
|
||||
final bool hasLocalPlaybackCandidate;
|
||||
|
||||
const TrackCollectionQuickActions({super.key, required this.track});
|
||||
const TrackCollectionQuickActions({
|
||||
super.key,
|
||||
required this.track,
|
||||
this.hasLocalPlaybackCandidate = false,
|
||||
});
|
||||
|
||||
static void showTrackOptionsSheet(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
Track track,
|
||||
) {
|
||||
Track track, {
|
||||
bool hasLocalPlaybackCandidate = false,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
@@ -29,7 +37,10 @@ class TrackCollectionQuickActions extends ConsumerWidget {
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(28)),
|
||||
),
|
||||
builder: (sheetContext) => _TrackOptionsSheet(track: track),
|
||||
builder: (sheetContext) => _TrackOptionsSheet(
|
||||
track: track,
|
||||
hasLocalPlaybackCandidate: hasLocalPlaybackCandidate,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +55,12 @@ class TrackCollectionQuickActions extends ConsumerWidget {
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () => showTrackOptionsSheet(context, ref, track),
|
||||
onPressed: () => showTrackOptionsSheet(
|
||||
context,
|
||||
ref,
|
||||
track,
|
||||
hasLocalPlaybackCandidate: hasLocalPlaybackCandidate,
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
constraints: const BoxConstraints(minWidth: 36, minHeight: 36),
|
||||
);
|
||||
@@ -53,8 +69,12 @@ class TrackCollectionQuickActions extends ConsumerWidget {
|
||||
|
||||
class _TrackOptionsSheet extends ConsumerWidget {
|
||||
final Track track;
|
||||
final bool hasLocalPlaybackCandidate;
|
||||
|
||||
const _TrackOptionsSheet({required this.track});
|
||||
const _TrackOptionsSheet({
|
||||
required this.track,
|
||||
required this.hasLocalPlaybackCandidate,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -69,6 +89,10 @@ class _TrackOptionsSheet extends ConsumerWidget {
|
||||
final allowQualityVariants = ref.watch(
|
||||
settingsProvider.select((settings) => settings.allowQualityVariants),
|
||||
);
|
||||
final qualityVariantAction = resolveQualityVariantMenuAction(
|
||||
allowQualityVariants: allowQualityVariants,
|
||||
hasLocalPlaybackCandidate: hasLocalPlaybackCandidate,
|
||||
);
|
||||
|
||||
return SafeArea(
|
||||
child: ConstrainedBox(
|
||||
@@ -168,23 +192,18 @@ class _TrackOptionsSheet extends ConsumerWidget {
|
||||
color: colorScheme.outlineVariant.withValues(alpha: 0.5),
|
||||
),
|
||||
|
||||
if (allowQualityVariants)
|
||||
if (qualityVariantAction == QualityVariantMenuAction.playLocal)
|
||||
_OptionTile(
|
||||
icon: Icons.play_arrow_rounded,
|
||||
title: context.l10n.trackMetadataPlay,
|
||||
onTap: () => _playLocal(context, ref),
|
||||
)
|
||||
else if (qualityVariantAction ==
|
||||
QualityVariantMenuAction.downloadAnotherQuality)
|
||||
_OptionTile(
|
||||
icon: Icons.download_outlined,
|
||||
title: context.l10n.trackOptionDownloadQualityVariant,
|
||||
onTap: () {
|
||||
final rootContext = Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).context;
|
||||
Navigator.pop(context);
|
||||
downloadSingleTrack(
|
||||
rootContext,
|
||||
ref,
|
||||
track,
|
||||
forceQualityPicker: true,
|
||||
);
|
||||
},
|
||||
onTap: () => _downloadQualityVariant(context, ref),
|
||||
),
|
||||
|
||||
_OptionTile(
|
||||
@@ -255,6 +274,23 @@ class _TrackOptionsSheet extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _downloadQualityVariant(BuildContext context, WidgetRef ref) {
|
||||
final rootContext = Navigator.of(context, rootNavigator: true).context;
|
||||
Navigator.pop(context);
|
||||
downloadSingleTrack(rootContext, ref, track, forceQualityPicker: true);
|
||||
}
|
||||
|
||||
Future<void> _playLocal(BuildContext context, WidgetRef ref) async {
|
||||
final rootContext = Navigator.of(context, rootNavigator: true).context;
|
||||
Navigator.pop(context);
|
||||
final played = await playLocalIfAvailable(rootContext, ref, track);
|
||||
if (!played && rootContext.mounted) {
|
||||
ScaffoldMessenger.of(rootContext).showSnackBar(
|
||||
SnackBar(content: Text(rootContext.l10n.snackbarFileNotFound)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _OptionTile extends StatelessWidget {
|
||||
|
||||
@@ -116,7 +116,10 @@ class TrackListTile extends ConsumerWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
PreviewButton(track: track),
|
||||
TrackCollectionQuickActions(track: track),
|
||||
TrackCollectionQuickActions(
|
||||
track: track,
|
||||
hasLocalPlaybackCandidate: isInHistory || isInLocalLibrary,
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () => _handleTap(
|
||||
@@ -129,6 +132,7 @@ class TrackListTile extends ConsumerWidget {
|
||||
context,
|
||||
ref,
|
||||
track,
|
||||
hasLocalPlaybackCandidate: isInHistory || isInLocalLibrary,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:spotiflac_android/widgets/track_collection_action_policy.dart';
|
||||
|
||||
void main() {
|
||||
group('quality variant track menu', () {
|
||||
test('is hidden when quality variants are disabled', () {
|
||||
expect(
|
||||
resolveQualityVariantMenuAction(
|
||||
allowQualityVariants: false,
|
||||
hasLocalPlaybackCandidate: true,
|
||||
),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
||||
test('offers another download when the track is not local', () {
|
||||
expect(
|
||||
resolveQualityVariantMenuAction(
|
||||
allowQualityVariants: true,
|
||||
hasLocalPlaybackCandidate: false,
|
||||
),
|
||||
QualityVariantMenuAction.downloadAnotherQuality,
|
||||
);
|
||||
});
|
||||
|
||||
test('swaps to local playback when row tap handles the download', () {
|
||||
expect(
|
||||
resolveQualityVariantMenuAction(
|
||||
allowQualityVariants: true,
|
||||
hasLocalPlaybackCandidate: true,
|
||||
),
|
||||
QualityVariantMenuAction.playLocal,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user