fix(artwork): stop memoizing posters that merely have no client yet

Distinguish an unresolvable URL from a failed load at the error-widget
boundary. A transiently null media client during a profile switch or
reconnect marked the primary poster dead in a process-global set, pinning the
item to fallback artwork for the rest of the session.
This commit is contained in:
edde746
2026-07-27 17:44:52 +02:00
parent 5f77da93d1
commit c64e6fc519
3 changed files with 68 additions and 8 deletions
+46
View File
@@ -209,6 +209,52 @@ void main() {
expect((fallback as OptimizedMediaImage).imagePath, albumArtwork);
expect(fallback.imageType, ImageType.square);
});
testWidgets('unresolved track artwork fallback is not memoized', (tester) async {
const trackArtwork = 'https://poster-memo.example/unresolved-track.jpg';
const albumArtwork = 'https://poster-memo.example/unresolved-album.jpg';
final item = testMediaItem(
id: 'track-with-unresolved-artwork',
backend: MediaBackend.jellyfin,
kind: MediaKind.track,
title: 'Track',
thumbPath: trackArtwork,
parentThumbPath: albumArtwork,
);
Future<void> pumpCard() => tester.pumpWidget(
_TestApp(child: MediaCard(item: item, width: 200, height: 194, forceGridMode: true, isOffline: true)),
);
await pumpCard();
final primaryFinder = find.descendant(of: find.byType(MediaCard), matching: find.byType(OptimizedMediaImage)).first;
final primary = tester.widget<OptimizedMediaImage>(primaryFinder);
expect(primary.imagePath, trackArtwork);
expect(primary.errorWidget, isNotNull);
final unresolvedFallback = primary.errorWidget!(
tester.element(primaryFinder),
trackArtwork,
const UnresolvedImageUrl(trackArtwork),
);
expect(unresolvedFallback, isA<OptimizedMediaImage>());
expect((unresolvedFallback as OptimizedMediaImage).imagePath, albumArtwork);
await pumpCard();
final unresolvedRebuild = tester.widget<OptimizedMediaImage>(primaryFinder);
expect(unresolvedRebuild.imagePath, trackArtwork);
final failedFallback = unresolvedRebuild.errorWidget!(
tester.element(primaryFinder),
trackArtwork,
StateError('decode failed'),
);
expect(failedFallback, isA<OptimizedMediaImage>());
expect((failedFallback as OptimizedMediaImage).imagePath, albumArtwork);
await pumpCard();
expect(tester.widget<OptimizedMediaImage>(primaryFinder).imagePath, albumArtwork);
});
}
class _TestApp extends StatelessWidget {