mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-19 14:07:26 +02:00
fix(stats): show artwork for top artists
This commit is contained in:
1 parent
82068263f2
commit
61708829fd
4 files changed
+113
-2
No files matched your search
@@ -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(
|
||||
|
||||
@@ -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>{};
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user