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 sortSongs(List 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'); }