feat(navigation): add go to album actions

This commit is contained in:
zarzet
2026-08-01 16:06:08 +07:00
parent 6a3a85f7c9
commit 645a324a5f
7 changed files with 254 additions and 22 deletions
+87
View File
@@ -5,9 +5,13 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show ScrollDirection;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/providers/download_history_provider.dart';
import 'package:spotiflac_android/providers/music_player_provider.dart';
import 'package:spotiflac_android/screens/downloaded_album_screen.dart';
import 'package:spotiflac_android/screens/local_album_screen.dart';
import 'package:spotiflac_android/services/library_database.dart';
import 'package:spotiflac_android/services/music_player_service.dart';
import 'package:spotiflac_android/utils/clickable_metadata.dart';
import 'package:spotiflac_android/utils/file_access.dart';
import 'package:spotiflac_android/utils/int_utils.dart';
import 'package:spotiflac_android/utils/lyrics_parser.dart';
@@ -729,6 +733,12 @@ class _NowPlayingScreenState extends ConsumerState<NowPlayingScreen> {
padding: const EdgeInsets.only(bottom: 16),
child: SettingsGroup(
children: [
if ((mediaItem.album ?? '').trim().isNotEmpty)
SettingsItem(
icon: Icons.album_outlined,
title: sheetContext.l10n.homeGoToAlbum,
onTap: () => Navigator.of(sheetContext).pop('album'),
),
SettingsItem(
icon: Icons.info_outline,
title: sheetContext.l10n.nowPlayingDetails,
@@ -751,6 +761,9 @@ class _NowPlayingScreenState extends ConsumerState<NowPlayingScreen> {
);
if (!mounted) return;
switch (action) {
case 'album':
await _goToCurrentAlbum(mediaItem: mediaItem, source: source);
break;
case 'details':
_showDetailsSheet(colorScheme);
break;
@@ -760,6 +773,80 @@ class _NowPlayingScreenState extends ConsumerState<NowPlayingScreen> {
}
}
Future<void> _goToCurrentAlbum({
required MediaItem mediaItem,
required String source,
}) async {
final albumName = (mediaItem.album ?? '').trim();
if (albumName.isEmpty) return;
// Prefer the stored collection so this action remains useful offline and
// opens the exact files the user is currently playing.
try {
final historyItem = source.isEmpty
? null
: await ref
.read(downloadHistoryProvider.notifier)
.getByFilePathAsync(source);
if (!mounted) return;
if (historyItem != null) {
final albumArtist = (historyItem.albumArtist ?? '').trim();
pushViaPreferredNavigator(
context,
(_) => DownloadedAlbumScreen(
albumName: historyItem.albumName,
artistName: albumArtist.isNotEmpty
? albumArtist
: historyItem.artistName,
coverUrl: historyItem.coverUrl,
),
);
return;
}
} catch (e) {
_log.w('Failed to resolve downloaded album: $e');
}
try {
final row = await LibraryDatabase.instance.getById(mediaItem.id);
if (!mounted) return;
if (row != null) {
final item = LocalLibraryItem.fromJson(row);
final rows = await LibraryDatabase.instance
.getQueueLocalAlbumTracksByKey(item.albumKey);
if (!mounted) return;
final tracks = rows
.map(LocalLibraryItem.fromJson)
.toList(growable: false);
if (tracks.isNotEmpty) {
final albumArtist = (item.albumArtist ?? '').trim();
pushViaPreferredNavigator(
context,
(_) => LocalAlbumScreen(
albumName: item.albumName,
artistName: albumArtist.isNotEmpty
? albumArtist
: item.artistName,
coverPath: item.coverPath,
tracks: tracks,
),
);
return;
}
}
} catch (e) {
_log.w('Failed to resolve local album: $e');
}
if (!mounted) return;
await navigateToAlbum(
context,
albumName: albumName,
artistName: mediaItem.artist,
coverUrl: mediaItem.artUri?.toString(),
);
}
Future<void> _shuffleLibrary(MusicPlayerController controller) async {
try {
final rows = await LibraryDatabase.instance.getAll();
+3
View File
@@ -45,6 +45,9 @@ import 'package:spotiflac_android/widgets/open_on_platform_sheet.dart';
import 'package:spotiflac_android/widgets/settings_group.dart';
import 'package:spotiflac_android/constants/music_services.dart';
import 'package:spotiflac_android/screens/collapsing_header_scroll_mixin.dart';
import 'package:spotiflac_android/screens/downloaded_album_screen.dart';
import 'package:spotiflac_android/screens/local_album_screen.dart';
import 'package:spotiflac_android/utils/clickable_metadata.dart';
part 'track_metadata_screen_cover.dart';
part 'track_metadata_screen_display.dart';
@@ -29,6 +29,12 @@ extension _TrackMetadataMenu on _TrackMetadataScreenState {
label: l10n.trackAddToQueue,
onTap: () => _enqueueThis(ref, playNext: false),
),
if (albumName.trim().isNotEmpty)
_MetadataOption(
icon: Icons.album_outlined,
label: l10n.homeGoToAlbum,
onTap: () => _goToStoredAlbum(screenContext),
),
_MetadataOption(
icon: Icons.copy_outlined,
label: l10n.trackCopyFilePath,
@@ -226,4 +232,55 @@ extension _TrackMetadataMenu on _TrackMetadataScreenState {
return placeholder();
}
Future<void> _goToStoredAlbum(BuildContext screenContext) async {
final resolvedAlbumArtist = (albumArtist ?? '').trim();
final artist = resolvedAlbumArtist.isNotEmpty
? resolvedAlbumArtist
: artistName;
if (!_isLocalItem) {
pushViaPreferredNavigator(
screenContext,
(_) => DownloadedAlbumScreen(
albumName: albumName,
artistName: artist,
coverUrl: _coverUrl,
),
);
return;
}
try {
final rows = await LibraryDatabase.instance.getQueueLocalAlbumTracksByKey(
_localLibraryItem!.albumKey,
);
if (!mounted || !screenContext.mounted) return;
final tracks = rows
.map(LocalLibraryItem.fromJson)
.toList(growable: false);
if (tracks.isNotEmpty) {
pushViaPreferredNavigator(
screenContext,
(_) => LocalAlbumScreen(
albumName: albumName,
artistName: artist,
coverPath: _localCoverPath,
tracks: tracks,
),
);
return;
}
} catch (e) {
_log.w('Failed to resolve local album: $e');
}
if (!mounted || !screenContext.mounted) return;
await navigateToAlbum(
screenContext,
albumName: albumName,
artistName: artist,
coverUrl: _coverUrl,
);
}
}