fix(stats): show artwork for top artists

This commit is contained in:
zarzet committed 2026-10-03 17:15:49 +07:00
1 parent 82068263f2
commit 61708829fd
4 files changed
+113 -2

No files matched your search

+22 -1
View File
@@ -177,8 +177,10 @@ class _ListeningStatisticsScreenState
}
List<Widget> _summarySlivers(BuildContext context, ListeningSummary data) {
final colors = Theme.of(context).colorScheme;
final artists = data.artists.entries.toList()
..sort((a, b) => b.value.compareTo(a.value));
final artistArtwork = data.artistArtwork;
final days = data.days.entries.toList()
..sort((a, b) => a.key.compareTo(b.key));
return [
@@ -275,7 +277,26 @@ class _ListeningStatisticsScreenState
children: [
for (final artist in artists.take(10))
ListTile(
leading: const Icon(Icons.person_outline),
leading: ExcludeSemantics(
child: ClipOval(
child: SizedBox.square(
dimension: 48,
child: artistArtwork[artist.key] == null
? ColoredBox(
color: colors.surfaceContainerHighest,
child: const Icon(Icons.person_outline),
)
: PlayerArtwork(
artUri: artistArtwork[artist.key],
colorScheme: colors,
cacheWidth:
(48 * MediaQuery.devicePixelRatioOf(context))
.ceil(),
iconSize: 24,
),
),
),
),
title: Text(artist.key),
subtitle: Text(
context.l10n.listeningStatsMinutes(
+18
View File
@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:spotiflac_android/services/sqlite_helpers.dart' as sqlite;
import 'package:spotiflac_android/utils/logger.dart';
import 'package:spotiflac_android/utils/string_utils.dart';
import 'package:sqflite/sqflite.dart';
final _log = AppLogger('ListeningStatistics');
@@ -50,6 +51,23 @@ class ListeningSummary {
final Map<String, int> artists;
final Map<String, int> days;
Map<String, String> get artistArtwork {
final artwork = <String, String>{};
final listeningTime = <String, int>{};
for (final total in tracks) {
final cover = normalizeCoverReference(total.track.artwork);
if (cover == null) continue;
final artist = total.track.artist;
if (listeningTime.containsKey(artist) &&
total.milliseconds <= listeningTime[artist]!) {
continue;
}
artwork[artist] = cover;
listeningTime[artist] = total.milliseconds;
}
return artwork;
}
factory ListeningSummary.fromRows(List<Map<String, Object?>> rows) {
final tracks = <String, ListeningTotal>{};
final artists = <String, int>{};
+19 -1
View File
@@ -10,6 +10,7 @@ import 'package:spotiflac_android/screens/weekly_releases_screen.dart';
import 'package:spotiflac_android/services/listening_statistics.dart';
import 'package:spotiflac_android/services/weekly_releases.dart';
import 'package:spotiflac_android/theme/mornye_theme.dart';
import 'package:spotiflac_android/widgets/player_artwork.dart';
class _Settings extends SettingsNotifier {
@override
@@ -43,7 +44,7 @@ void main() {
'album': 'Album',
'milliseconds': 120000,
'plays': 1,
'artwork': null,
'artwork': '/covers/album.jpg',
},
]);
}),
@@ -57,6 +58,23 @@ void main() {
await tester.pumpAndSettle();
expect(requested.last.days, 0);
expect(requested.last.year, DateTime.now().year);
final artistTile = find.byWidgetPredicate(
(widget) =>
widget is ListTile &&
widget.title is Text &&
(widget.title! as Text).data == 'Artist',
);
await tester.scrollUntilVisible(artistTile, 150);
await tester.pumpAndSettle();
final artistArtwork = find.descendant(
of: artistTile,
matching: find.byType(PlayerArtwork),
);
expect(artistArtwork, findsOneWidget);
expect(
tester.widget<PlayerArtwork>(artistArtwork).artUri,
'/covers/album.jpg',
);
expect(tester.takeException(), isNull);
},
);
+54
View File
@@ -172,4 +172,58 @@ void main() {
expect(summary.artists, {'Artist': 120000});
expect(summary.days.length, 2);
});
test(
'artist artwork uses the most listened track with an available cover',
() {
final summary = ListeningSummary(
tracks: [
ListeningTotal(_one, 180000, 3),
ListeningTotal(
const ListeningTrack(
key: 'other',
title: 'Other',
artist: 'Other Artist',
album: 'Other Album',
artwork: 'https://example.com/other.jpg',
),
60000,
1,
),
ListeningTotal(
const ListeningTrack(
key: 'less',
title: 'Less',
artist: 'Artist',
album: 'Another Album',
artwork: 'https://example.com/less.jpg',
),
30000,
1,
),
ListeningTotal(
const ListeningTrack(
key: 'more',
title: 'More',
artist: 'Artist',
album: 'Album',
artwork: ' file:///covers/album.jpg ',
),
120000,
2,
),
],
);
expect(summary.artistArtwork, {
'Artist': 'file:///covers/album.jpg',
'Other Artist': 'https://example.com/other.jpg',
});
expect(
ListeningSummary(
tracks: [ListeningTotal(_one, 60000, 1)],
).artistArtwork,
isEmpty,
);
},
);
}