Neue Melo-UI Phase 1: 4 Tabs (Meine Musik/Suche/Favoriten/Download) + Sortierung
- Meine Musik als Startbildschirm ohne Topbar: Kopfzeile (Einstellungen, Suche, Musikerkennung), Schnellzugriffe, Shuffle + Sortieren, Liederliste - Sortierung nach Zuletzt hinzugefuegt / Name (A-Z-#) / Wie oft abgespielt, auf- und absteigend, pro Liste gespeichert (SortStore) - Favoriten als eigener Tab mit gleicher Shuffle-/Sortier-Leiste - Download-Tab uebernimmt den Navidrome-Server-Browser - DB-Schema 3: playCount, gezaehlt beim Titelstart - Theme auf #0B0B10 / #c0392b - Scan-Aktionen aus der entfallenen Topbar jetzt in den Einstellungen Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011snPXPafPC5i87o68H14W7
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import 'database.dart';
|
||||
|
||||
/// Sortier-Kriterien der Liederlisten (Meine Musik + Favoriten).
|
||||
enum SortMode { dateAdded, name, playCount }
|
||||
|
||||
extension SortModeLabel on SortMode {
|
||||
String get label => switch (this) {
|
||||
SortMode.dateAdded => 'Zuletzt hinzugefügt',
|
||||
SortMode.name => 'Name (A–Z–#)',
|
||||
SortMode.playCount => 'Wie oft abgespielt',
|
||||
};
|
||||
}
|
||||
|
||||
/// Richtung, in der ein Modus üblicherweise gelesen wird: neueste bzw.
|
||||
/// meistgespielte Songs zuerst, Namen dagegen von A nach Z.
|
||||
bool defaultAscending(SortMode mode) => mode == SortMode.name;
|
||||
|
||||
/// Sortiert eine Kopie von [songs]. Bei gleichem Sortierwert entscheidet der
|
||||
/// Titel, damit die Reihenfolge zwischen Aufrufen stabil bleibt.
|
||||
List<Song> sortSongs(List<Song> songs, SortMode mode, {required bool ascending}) {
|
||||
final sorted = [...songs];
|
||||
sorted.sort((a, b) {
|
||||
final cmp = switch (mode) {
|
||||
SortMode.dateAdded => a.dateAddedMs.compareTo(b.dateAddedMs),
|
||||
SortMode.playCount => a.playCount.compareTo(b.playCount),
|
||||
SortMode.name => _compareNames(a.title, b.title),
|
||||
};
|
||||
if (cmp != 0) return ascending ? cmp : -cmp;
|
||||
// Tiebreak immer aufsteigend nach Titel — sonst wäre die Reihenfolge
|
||||
// gleichwertiger Songs beim Umschalten der Richtung willkürlich.
|
||||
return _compareNames(a.title, b.title);
|
||||
});
|
||||
return sorted;
|
||||
}
|
||||
|
||||
/// A–Z zuerst, alles was nicht mit einem Buchstaben beginnt (Ziffern,
|
||||
/// Sonderzeichen) landet unter "#" am Ende.
|
||||
int _compareNames(String a, String b) {
|
||||
final keyA = _sortKey(a);
|
||||
final keyB = _sortKey(b);
|
||||
final letterA = _startsWithLetter(keyA);
|
||||
final letterB = _startsWithLetter(keyB);
|
||||
if (letterA != letterB) return letterA ? -1 : 1;
|
||||
return keyA.compareTo(keyB);
|
||||
}
|
||||
|
||||
final _letter = RegExp(r'^\p{L}', unicode: true);
|
||||
|
||||
bool _startsWithLetter(String s) => _letter.hasMatch(s);
|
||||
|
||||
/// Kleinschreibung + Umlaute auf ihren Grundbuchstaben, damit "Ärger"
|
||||
/// zwischen "Anfang" und "Berg" steht und nicht hinter "Zug".
|
||||
String _sortKey(String title) {
|
||||
return title
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replaceAll('ä', 'a')
|
||||
.replaceAll('ö', 'o')
|
||||
.replaceAll('ü', 'u')
|
||||
.replaceAll('ß', 'ss');
|
||||
}
|
||||
Reference in New Issue
Block a user