Fix: Review-Findings korrigiert (Tag-Leiste, Profil, Singleton u.a.)
CRIT: - Tag-Leiste: Toggle-Kopf 'Tags & Filter' eingebaut (war unerreichbar) + TagStats - Profil: Cloud-Count vor Dialog aufloesen (kein 'Instance of Future' mehr) HIGH: - CloudEinstellungen: toten Sync-Timer entfernt (echter Timer im ViewModel) - StatistikCard + RecentWidget jetzt eingebaut (gesamtMB/gesamtMin endlich genutzt) - CloudService als Singleton (konsistenter Token-Zustand in allen Services) MED/LOW: - Playlist-Erkennung nur noch via list= Parameter - Player: fehlende Quelle wird geloggt statt still - song_tile: null-ID-Guard vor Tag-Dialog - Scanner-Log mit Exception-Objekt - FavoritenService: anzahlFavoriten() fuer StatistikCard
This commit is contained in:
@@ -118,7 +118,7 @@ class _PlaylistSheetState extends State<PlaylistSheet> {
|
||||
isScrollControlled: true,
|
||||
builder: (_) => SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
child: _PlaylistDetail(p: p, songs: songs, vm: widget.vm, onChanged: _laden),
|
||||
child: _PlaylistDetail(p: p, initialSongs: songs, vm: widget.vm, onChanged: _laden),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -172,19 +172,32 @@ class _PlaylistSheetState extends State<PlaylistSheet> {
|
||||
}
|
||||
|
||||
/// Detailansicht einer Playlist mit Songs
|
||||
class _PlaylistDetail extends StatelessWidget {
|
||||
class _PlaylistDetail extends StatefulWidget {
|
||||
final Playlist p;
|
||||
final List<Song> songs;
|
||||
final List<Song> initialSongs;
|
||||
final MeloHomeViewModel vm;
|
||||
final VoidCallback onChanged;
|
||||
|
||||
const _PlaylistDetail({
|
||||
required this.p,
|
||||
required this.songs,
|
||||
required this.initialSongs,
|
||||
required this.vm,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_PlaylistDetail> createState() => _PlaylistDetailState();
|
||||
}
|
||||
|
||||
class _PlaylistDetailState extends State<_PlaylistDetail> {
|
||||
late List<Song> _songs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_songs = List<Song>.from(widget.initialSongs);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
@@ -209,18 +222,29 @@ class _PlaylistDetail extends StatelessWidget {
|
||||
child: const Icon(Icons.arrow_back, size: 20, color: MeloTheme.rot),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(p.name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white)),
|
||||
Text(widget.p.name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white)),
|
||||
const Spacer(),
|
||||
Text('${songs.length} Songs', style: const TextStyle(fontSize: 12, color: MeloTheme.textSekundaer)),
|
||||
Text('${_songs.length} Songs', style: const TextStyle(fontSize: 12, color: MeloTheme.textSekundaer)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Expanded(
|
||||
child: songs.isEmpty
|
||||
child: _songs.isEmpty
|
||||
? const Center(child: Text('Playlist ist leer', style: TextStyle(fontSize: 12, color: MeloTheme.textSekundaer)))
|
||||
: ListView.builder(
|
||||
itemCount: songs.length,
|
||||
itemBuilder: (_, i) => _songTile(context, songs[i]),
|
||||
: ReorderableListView.builder(
|
||||
itemCount: _songs.length,
|
||||
onReorder: (oldIndex, newIndex) async {
|
||||
if (newIndex > oldIndex) newIndex--;
|
||||
setState(() {
|
||||
final moved = _songs.removeAt(oldIndex);
|
||||
_songs.insert(newIndex, moved);
|
||||
});
|
||||
if (widget.p.id != null) {
|
||||
await widget.vm.playlists.reihenfolgeSpeichern(widget.p.id!, _songs);
|
||||
}
|
||||
widget.onChanged();
|
||||
},
|
||||
itemBuilder: (_, i) => _songTile(context, _songs[i]),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -230,6 +254,7 @@ class _PlaylistDetail extends StatelessWidget {
|
||||
|
||||
Widget _songTile(BuildContext context, Song song) {
|
||||
return Container(
|
||||
key: ValueKey(song.id ?? song.dateiPfad),
|
||||
margin: const EdgeInsets.only(bottom: 4),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
@@ -249,7 +274,10 @@ class _PlaylistDetail extends StatelessWidget {
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () { Navigator.pop(context); vm.spieleSong(song); },
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
widget.vm.spieleSong(song, warteschlange: _songs);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -261,9 +289,11 @@ class _PlaylistDetail extends StatelessWidget {
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
await vm.playlists.songEntfernen(p.id!, song.id!);
|
||||
onChanged();
|
||||
if (context.mounted) Navigator.pop(context);
|
||||
await widget.vm.playlists.songEntfernen(widget.p.id!, song.id!);
|
||||
setState(() {
|
||||
_songs.removeWhere((s) => s.id == song.id);
|
||||
});
|
||||
widget.onChanged();
|
||||
},
|
||||
child: const Icon(Icons.remove_circle_outline, size: 18, color: MeloTheme.textSekundaer),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user