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:
2026-08-20 18:00:53 +02:00
co-authored by Claude Opus 5
parent 5d08da1086
commit 2667613a2e
24 changed files with 1413 additions and 372 deletions
+70 -2
View File
@@ -113,6 +113,18 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, Song> {
),
defaultValue: const Constant(false),
);
static const VerificationMeta _playCountMeta = const VerificationMeta(
'playCount',
);
@override
late final GeneratedColumn<int> playCount = GeneratedColumn<int>(
'play_count',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultValue: const Constant(0),
);
@override
List<GeneratedColumn> get $columns => [
id,
@@ -125,6 +137,7 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, Song> {
dateAddedMs,
updatedAtMs,
deleted,
playCount,
];
@override
String get aliasedName => _alias ?? actualTableName;
@@ -211,6 +224,12 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, Song> {
deleted.isAcceptableOrUnknown(data['deleted']!, _deletedMeta),
);
}
if (data.containsKey('play_count')) {
context.handle(
_playCountMeta,
playCount.isAcceptableOrUnknown(data['play_count']!, _playCountMeta),
);
}
return context;
}
@@ -260,6 +279,10 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, Song> {
DriftSqlType.bool,
data['${effectivePrefix}deleted'],
)!,
playCount: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}play_count'],
)!,
);
}
@@ -280,6 +303,10 @@ class Song extends DataClass implements Insertable<Song> {
final int dateAddedMs;
final int updatedAtMs;
final bool deleted;
/// Wie oft der Song vollständig gestartet wurde — Grundlage für die
/// Sortierung "Wie oft abgespielt".
final int playCount;
const Song({
required this.id,
required this.path,
@@ -291,6 +318,7 @@ class Song extends DataClass implements Insertable<Song> {
required this.dateAddedMs,
required this.updatedAtMs,
required this.deleted,
required this.playCount,
});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
@@ -313,6 +341,7 @@ class Song extends DataClass implements Insertable<Song> {
map['date_added_ms'] = Variable<int>(dateAddedMs);
map['updated_at_ms'] = Variable<int>(updatedAtMs);
map['deleted'] = Variable<bool>(deleted);
map['play_count'] = Variable<int>(playCount);
return map;
}
@@ -336,6 +365,7 @@ class Song extends DataClass implements Insertable<Song> {
dateAddedMs: Value(dateAddedMs),
updatedAtMs: Value(updatedAtMs),
deleted: Value(deleted),
playCount: Value(playCount),
);
}
@@ -355,6 +385,7 @@ class Song extends DataClass implements Insertable<Song> {
dateAddedMs: serializer.fromJson<int>(json['dateAddedMs']),
updatedAtMs: serializer.fromJson<int>(json['updatedAtMs']),
deleted: serializer.fromJson<bool>(json['deleted']),
playCount: serializer.fromJson<int>(json['playCount']),
);
}
@override
@@ -371,6 +402,7 @@ class Song extends DataClass implements Insertable<Song> {
'dateAddedMs': serializer.toJson<int>(dateAddedMs),
'updatedAtMs': serializer.toJson<int>(updatedAtMs),
'deleted': serializer.toJson<bool>(deleted),
'playCount': serializer.toJson<int>(playCount),
};
}
@@ -385,6 +417,7 @@ class Song extends DataClass implements Insertable<Song> {
int? dateAddedMs,
int? updatedAtMs,
bool? deleted,
int? playCount,
}) => Song(
id: id ?? this.id,
path: path ?? this.path,
@@ -396,6 +429,7 @@ class Song extends DataClass implements Insertable<Song> {
dateAddedMs: dateAddedMs ?? this.dateAddedMs,
updatedAtMs: updatedAtMs ?? this.updatedAtMs,
deleted: deleted ?? this.deleted,
playCount: playCount ?? this.playCount,
);
Song copyWithCompanion(SongsCompanion data) {
return Song(
@@ -415,6 +449,7 @@ class Song extends DataClass implements Insertable<Song> {
? data.updatedAtMs.value
: this.updatedAtMs,
deleted: data.deleted.present ? data.deleted.value : this.deleted,
playCount: data.playCount.present ? data.playCount.value : this.playCount,
);
}
@@ -430,7 +465,8 @@ class Song extends DataClass implements Insertable<Song> {
..write('coverPath: $coverPath, ')
..write('dateAddedMs: $dateAddedMs, ')
..write('updatedAtMs: $updatedAtMs, ')
..write('deleted: $deleted')
..write('deleted: $deleted, ')
..write('playCount: $playCount')
..write(')'))
.toString();
}
@@ -447,6 +483,7 @@ class Song extends DataClass implements Insertable<Song> {
dateAddedMs,
updatedAtMs,
deleted,
playCount,
);
@override
bool operator ==(Object other) =>
@@ -461,7 +498,8 @@ class Song extends DataClass implements Insertable<Song> {
other.coverPath == this.coverPath &&
other.dateAddedMs == this.dateAddedMs &&
other.updatedAtMs == this.updatedAtMs &&
other.deleted == this.deleted);
other.deleted == this.deleted &&
other.playCount == this.playCount);
}
class SongsCompanion extends UpdateCompanion<Song> {
@@ -475,6 +513,7 @@ class SongsCompanion extends UpdateCompanion<Song> {
final Value<int> dateAddedMs;
final Value<int> updatedAtMs;
final Value<bool> deleted;
final Value<int> playCount;
final Value<int> rowid;
const SongsCompanion({
this.id = const Value.absent(),
@@ -487,6 +526,7 @@ class SongsCompanion extends UpdateCompanion<Song> {
this.dateAddedMs = const Value.absent(),
this.updatedAtMs = const Value.absent(),
this.deleted = const Value.absent(),
this.playCount = const Value.absent(),
this.rowid = const Value.absent(),
});
SongsCompanion.insert({
@@ -500,6 +540,7 @@ class SongsCompanion extends UpdateCompanion<Song> {
required int dateAddedMs,
required int updatedAtMs,
this.deleted = const Value.absent(),
this.playCount = const Value.absent(),
this.rowid = const Value.absent(),
}) : id = Value(id),
path = Value(path),
@@ -517,6 +558,7 @@ class SongsCompanion extends UpdateCompanion<Song> {
Expression<int>? dateAddedMs,
Expression<int>? updatedAtMs,
Expression<bool>? deleted,
Expression<int>? playCount,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
@@ -530,6 +572,7 @@ class SongsCompanion extends UpdateCompanion<Song> {
if (dateAddedMs != null) 'date_added_ms': dateAddedMs,
if (updatedAtMs != null) 'updated_at_ms': updatedAtMs,
if (deleted != null) 'deleted': deleted,
if (playCount != null) 'play_count': playCount,
if (rowid != null) 'rowid': rowid,
});
}
@@ -545,6 +588,7 @@ class SongsCompanion extends UpdateCompanion<Song> {
Value<int>? dateAddedMs,
Value<int>? updatedAtMs,
Value<bool>? deleted,
Value<int>? playCount,
Value<int>? rowid,
}) {
return SongsCompanion(
@@ -558,6 +602,7 @@ class SongsCompanion extends UpdateCompanion<Song> {
dateAddedMs: dateAddedMs ?? this.dateAddedMs,
updatedAtMs: updatedAtMs ?? this.updatedAtMs,
deleted: deleted ?? this.deleted,
playCount: playCount ?? this.playCount,
rowid: rowid ?? this.rowid,
);
}
@@ -595,6 +640,9 @@ class SongsCompanion extends UpdateCompanion<Song> {
if (deleted.present) {
map['deleted'] = Variable<bool>(deleted.value);
}
if (playCount.present) {
map['play_count'] = Variable<int>(playCount.value);
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
@@ -614,6 +662,7 @@ class SongsCompanion extends UpdateCompanion<Song> {
..write('dateAddedMs: $dateAddedMs, ')
..write('updatedAtMs: $updatedAtMs, ')
..write('deleted: $deleted, ')
..write('playCount: $playCount, ')
..write('rowid: $rowid')
..write(')'))
.toString();
@@ -2223,6 +2272,7 @@ typedef $$SongsTableCreateCompanionBuilder =
required int dateAddedMs,
required int updatedAtMs,
Value<bool> deleted,
Value<int> playCount,
Value<int> rowid,
});
typedef $$SongsTableUpdateCompanionBuilder =
@@ -2237,6 +2287,7 @@ typedef $$SongsTableUpdateCompanionBuilder =
Value<int> dateAddedMs,
Value<int> updatedAtMs,
Value<bool> deleted,
Value<int> playCount,
Value<int> rowid,
});
@@ -2359,6 +2410,11 @@ class $$SongsTableFilterComposer extends Composer<_$MeloDb, $SongsTable> {
builder: (column) => ColumnFilters(column),
);
ColumnFilters<int> get playCount => $composableBuilder(
column: $table.playCount,
builder: (column) => ColumnFilters(column),
);
Expression<bool> playlistSongsRefs(
Expression<bool> Function($$PlaylistSongsTableFilterComposer f) f,
) {
@@ -2492,6 +2548,11 @@ class $$SongsTableOrderingComposer extends Composer<_$MeloDb, $SongsTable> {
column: $table.deleted,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<int> get playCount => $composableBuilder(
column: $table.playCount,
builder: (column) => ColumnOrderings(column),
);
}
class $$SongsTableAnnotationComposer extends Composer<_$MeloDb, $SongsTable> {
@@ -2538,6 +2599,9 @@ class $$SongsTableAnnotationComposer extends Composer<_$MeloDb, $SongsTable> {
GeneratedColumn<bool> get deleted =>
$composableBuilder(column: $table.deleted, builder: (column) => column);
GeneratedColumn<int> get playCount =>
$composableBuilder(column: $table.playCount, builder: (column) => column);
Expression<T> playlistSongsRefs<T extends Object>(
Expression<T> Function($$PlaylistSongsTableAnnotationComposer a) f,
) {
@@ -2656,6 +2720,7 @@ class $$SongsTableTableManager
Value<int> dateAddedMs = const Value.absent(),
Value<int> updatedAtMs = const Value.absent(),
Value<bool> deleted = const Value.absent(),
Value<int> playCount = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) => SongsCompanion(
id: id,
@@ -2668,6 +2733,7 @@ class $$SongsTableTableManager
dateAddedMs: dateAddedMs,
updatedAtMs: updatedAtMs,
deleted: deleted,
playCount: playCount,
rowid: rowid,
),
createCompanionCallback:
@@ -2682,6 +2748,7 @@ class $$SongsTableTableManager
required int dateAddedMs,
required int updatedAtMs,
Value<bool> deleted = const Value.absent(),
Value<int> playCount = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) => SongsCompanion.insert(
id: id,
@@ -2694,6 +2761,7 @@ class $$SongsTableTableManager
dateAddedMs: dateAddedMs,
updatedAtMs: updatedAtMs,
deleted: deleted,
playCount: playCount,
rowid: rowid,
),
withReferenceMapper: (p0) => p0