mirror of
https://github.com/immich-app/immich.git
synced 2026-04-12 04:02:15 -04:00
fix(mobile): fix flutter cache eviction on thumbnails (#27663)
* fix: add markFinished parameter to loadRequest and loadCodecRequest methods * update loadRequest and loadCodecRequest methods to use isFinal * Apply suggestions from code review Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com> * remove redundant check * fix: ensure isFinished is set correctly during cache eviction * formatting --------- Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
This commit is contained in:
parent
bc400d68ac
commit
d39e7da10d
@ -51,7 +51,7 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
||||
return null;
|
||||
}
|
||||
|
||||
Stream<ImageInfo> loadRequest(ImageRequest request, ImageDecoderCallback decode, {bool evictOnError = true}) async* {
|
||||
Stream<ImageInfo> loadRequest(ImageRequest request, ImageDecoderCallback decode, {required bool isFinal}) async* {
|
||||
if (isCancelled) {
|
||||
this.request = null;
|
||||
return;
|
||||
@ -59,21 +59,18 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
||||
|
||||
try {
|
||||
final image = await request.load(decode);
|
||||
if (isCancelled) {
|
||||
return;
|
||||
}
|
||||
if (image == null && evictOnError) {
|
||||
PaintingBinding.instance.imageCache.evict(this);
|
||||
return;
|
||||
} else if (image == null) {
|
||||
if (isCancelled || image == null) {
|
||||
image?.dispose();
|
||||
return;
|
||||
}
|
||||
isFinished = isFinal;
|
||||
yield image;
|
||||
} catch (e, stack) {
|
||||
if (isCancelled) {
|
||||
return;
|
||||
}
|
||||
if (evictOnError) {
|
||||
if (isFinal) {
|
||||
isFinished = true;
|
||||
PaintingBinding.instance.imageCache.evict(this);
|
||||
rethrow;
|
||||
}
|
||||
@ -83,7 +80,7 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
||||
}
|
||||
}
|
||||
|
||||
Future<ui.Codec?> loadCodecRequest(ImageRequest request) async {
|
||||
Future<ui.Codec?> loadCodecRequest(ImageRequest request, {required bool isFinal}) async {
|
||||
if (isCancelled) {
|
||||
this.request = null;
|
||||
return null;
|
||||
@ -91,20 +88,19 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
||||
|
||||
try {
|
||||
final codec = await request.loadCodec();
|
||||
if (isCancelled) {
|
||||
if (isCancelled || codec == null) {
|
||||
codec?.dispose();
|
||||
return null;
|
||||
}
|
||||
if (codec == null) {
|
||||
PaintingBinding.instance.imageCache.evict(this);
|
||||
return null;
|
||||
}
|
||||
isFinished = isFinal;
|
||||
return codec;
|
||||
} catch (e) {
|
||||
if (!isCancelled) {
|
||||
if (isFinal) {
|
||||
isFinished = true;
|
||||
PaintingBinding.instance.imageCache.evict(this);
|
||||
rethrow;
|
||||
}
|
||||
rethrow;
|
||||
return null;
|
||||
} finally {
|
||||
this.request = null;
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ class LocalThumbProvider extends CancellableImageProvider<LocalThumbProvider>
|
||||
|
||||
Stream<ImageInfo> _codec(LocalThumbProvider key, ImageDecoderCallback decode) {
|
||||
final request = this.request = LocalImageRequest(localId: key.id, size: key.size, assetType: key.assetType);
|
||||
return loadRequest(request, decode);
|
||||
return loadRequest(request, decode, isFinal: true);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -103,16 +103,16 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
||||
return;
|
||||
}
|
||||
|
||||
final loadOriginal = Store.get(StoreKey.loadOriginal, false);
|
||||
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
|
||||
var request = this.request = LocalImageRequest(
|
||||
localId: key.id,
|
||||
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
|
||||
assetType: key.assetType,
|
||||
);
|
||||
yield* loadRequest(request, decode);
|
||||
yield* loadRequest(request, decode, isFinal: !loadOriginal);
|
||||
|
||||
if (!Store.get(StoreKey.loadOriginal, false)) {
|
||||
isFinished = true;
|
||||
if (!loadOriginal) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -122,8 +122,7 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
||||
|
||||
request = this.request = LocalImageRequest(localId: key.id, assetType: key.assetType, size: Size.zero);
|
||||
|
||||
yield* loadRequest(request, decode);
|
||||
isFinished = true;
|
||||
yield* loadRequest(request, decode, isFinal: true);
|
||||
}
|
||||
|
||||
Stream<Object> _animatedCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async* {
|
||||
@ -139,7 +138,7 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
||||
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
|
||||
assetType: key.assetType,
|
||||
);
|
||||
yield* loadRequest(previewRequest, decode);
|
||||
yield* loadRequest(previewRequest, decode, isFinal: false);
|
||||
|
||||
if (isCancelled) {
|
||||
return;
|
||||
@ -147,13 +146,12 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
||||
|
||||
// always try original for animated, since previews don't support animation
|
||||
final originalRequest = request = LocalImageRequest(localId: key.id, size: Size.zero, assetType: key.assetType);
|
||||
final codec = await loadCodecRequest(originalRequest);
|
||||
final codec = await loadCodecRequest(originalRequest, isFinal: true);
|
||||
if (codec == null) {
|
||||
if (isCancelled) return;
|
||||
throw StateError('Failed to load animated codec for local asset ${key.id}');
|
||||
}
|
||||
yield codec;
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -38,7 +38,7 @@ class RemoteImageProvider extends CancellableImageProvider<RemoteImageProvider>
|
||||
|
||||
Stream<ImageInfo> _codec(RemoteImageProvider key, ImageDecoderCallback decode) {
|
||||
final request = this.request = RemoteImageRequest(uri: key.url);
|
||||
return loadRequest(request, decode);
|
||||
return loadRequest(request, decode, isFinal: true);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -112,10 +112,9 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
||||
uri: getThumbnailUrlForRemoteId(key.assetId, type: AssetMediaSize.preview, thumbhash: key.thumbhash),
|
||||
);
|
||||
final loadOriginal = assetType == AssetType.image && AppSetting.get(Setting.loadOriginal);
|
||||
yield* loadRequest(previewRequest, decode, evictOnError: !loadOriginal);
|
||||
yield* loadRequest(previewRequest, decode, isFinal: !loadOriginal);
|
||||
|
||||
if (!loadOriginal) {
|
||||
isFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -124,8 +123,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
||||
}
|
||||
|
||||
final originalRequest = request = RemoteImageRequest(uri: getOriginalUrlForRemoteId(key.assetId));
|
||||
yield* loadRequest(originalRequest, decode);
|
||||
isFinished = true;
|
||||
yield* loadRequest(originalRequest, decode, isFinal: true);
|
||||
}
|
||||
|
||||
Stream<Object> _animatedCodec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* {
|
||||
@ -138,7 +136,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
||||
final previewRequest = request = RemoteImageRequest(
|
||||
uri: getThumbnailUrlForRemoteId(key.assetId, type: AssetMediaSize.preview, thumbhash: key.thumbhash),
|
||||
);
|
||||
yield* loadRequest(previewRequest, decode, evictOnError: false);
|
||||
yield* loadRequest(previewRequest, decode, isFinal: false);
|
||||
|
||||
if (isCancelled) {
|
||||
return;
|
||||
@ -146,7 +144,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
||||
|
||||
// always try original for animated, since previews don't support animation
|
||||
final originalRequest = request = RemoteImageRequest(uri: getOriginalUrlForRemoteId(key.assetId));
|
||||
final codec = await loadCodecRequest(originalRequest);
|
||||
final codec = await loadCodecRequest(originalRequest, isFinal: true);
|
||||
if (codec == null) {
|
||||
if (isCancelled) {
|
||||
return;
|
||||
@ -154,7 +152,6 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
||||
throw StateError('Failed to load animated codec for asset ${key.assetId}');
|
||||
}
|
||||
yield codec;
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -22,7 +22,7 @@ class ThumbHashProvider extends CancellableImageProvider<ThumbHashProvider>
|
||||
|
||||
Stream<ImageInfo> _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) {
|
||||
final request = this.request = ThumbhashImageRequest(thumbhash: key.thumbHash);
|
||||
return loadRequest(request, decode);
|
||||
return loadRequest(request, decode, isFinal: true);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user