mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03: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:
@@ -51,7 +51,7 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
|||||||
return null;
|
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) {
|
if (isCancelled) {
|
||||||
this.request = null;
|
this.request = null;
|
||||||
return;
|
return;
|
||||||
@@ -59,21 +59,18 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final image = await request.load(decode);
|
final image = await request.load(decode);
|
||||||
if (isCancelled) {
|
if (isCancelled || image == null) {
|
||||||
return;
|
image?.dispose();
|
||||||
}
|
|
||||||
if (image == null && evictOnError) {
|
|
||||||
PaintingBinding.instance.imageCache.evict(this);
|
|
||||||
return;
|
|
||||||
} else if (image == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
isFinished = isFinal;
|
||||||
yield image;
|
yield image;
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (evictOnError) {
|
if (isFinal) {
|
||||||
|
isFinished = true;
|
||||||
PaintingBinding.instance.imageCache.evict(this);
|
PaintingBinding.instance.imageCache.evict(this);
|
||||||
rethrow;
|
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) {
|
if (isCancelled) {
|
||||||
this.request = null;
|
this.request = null;
|
||||||
return null;
|
return null;
|
||||||
@@ -91,20 +88,19 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final codec = await request.loadCodec();
|
final codec = await request.loadCodec();
|
||||||
if (isCancelled) {
|
if (isCancelled || codec == null) {
|
||||||
codec?.dispose();
|
codec?.dispose();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (codec == null) {
|
isFinished = isFinal;
|
||||||
PaintingBinding.instance.imageCache.evict(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return codec;
|
return codec;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!isCancelled) {
|
if (isFinal) {
|
||||||
|
isFinished = true;
|
||||||
PaintingBinding.instance.imageCache.evict(this);
|
PaintingBinding.instance.imageCache.evict(this);
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
rethrow;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
this.request = null;
|
this.request = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class LocalThumbProvider extends CancellableImageProvider<LocalThumbProvider>
|
|||||||
|
|
||||||
Stream<ImageInfo> _codec(LocalThumbProvider key, ImageDecoderCallback decode) {
|
Stream<ImageInfo> _codec(LocalThumbProvider key, ImageDecoderCallback decode) {
|
||||||
final request = this.request = LocalImageRequest(localId: key.id, size: key.size, assetType: key.assetType);
|
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
|
@override
|
||||||
@@ -103,16 +103,16 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final loadOriginal = Store.get(StoreKey.loadOriginal, false);
|
||||||
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
|
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
|
||||||
var request = this.request = LocalImageRequest(
|
var request = this.request = LocalImageRequest(
|
||||||
localId: key.id,
|
localId: key.id,
|
||||||
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
|
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
|
||||||
assetType: key.assetType,
|
assetType: key.assetType,
|
||||||
);
|
);
|
||||||
yield* loadRequest(request, decode);
|
yield* loadRequest(request, decode, isFinal: !loadOriginal);
|
||||||
|
|
||||||
if (!Store.get(StoreKey.loadOriginal, false)) {
|
if (!loadOriginal) {
|
||||||
isFinished = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,8 +122,7 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
|||||||
|
|
||||||
request = this.request = LocalImageRequest(localId: key.id, assetType: key.assetType, size: Size.zero);
|
request = this.request = LocalImageRequest(localId: key.id, assetType: key.assetType, size: Size.zero);
|
||||||
|
|
||||||
yield* loadRequest(request, decode);
|
yield* loadRequest(request, decode, isFinal: true);
|
||||||
isFinished = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Object> _animatedCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async* {
|
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),
|
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
|
||||||
assetType: key.assetType,
|
assetType: key.assetType,
|
||||||
);
|
);
|
||||||
yield* loadRequest(previewRequest, decode);
|
yield* loadRequest(previewRequest, decode, isFinal: false);
|
||||||
|
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
@@ -147,13 +146,12 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
|||||||
|
|
||||||
// always try original for animated, since previews don't support animation
|
// 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 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 (codec == null) {
|
||||||
if (isCancelled) return;
|
if (isCancelled) return;
|
||||||
throw StateError('Failed to load animated codec for local asset ${key.id}');
|
throw StateError('Failed to load animated codec for local asset ${key.id}');
|
||||||
}
|
}
|
||||||
yield codec;
|
yield codec;
|
||||||
isFinished = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class RemoteImageProvider extends CancellableImageProvider<RemoteImageProvider>
|
|||||||
|
|
||||||
Stream<ImageInfo> _codec(RemoteImageProvider key, ImageDecoderCallback decode) {
|
Stream<ImageInfo> _codec(RemoteImageProvider key, ImageDecoderCallback decode) {
|
||||||
final request = this.request = RemoteImageRequest(uri: key.url);
|
final request = this.request = RemoteImageRequest(uri: key.url);
|
||||||
return loadRequest(request, decode);
|
return loadRequest(request, decode, isFinal: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -112,10 +112,9 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
|||||||
uri: getThumbnailUrlForRemoteId(key.assetId, type: AssetMediaSize.preview, thumbhash: key.thumbhash),
|
uri: getThumbnailUrlForRemoteId(key.assetId, type: AssetMediaSize.preview, thumbhash: key.thumbhash),
|
||||||
);
|
);
|
||||||
final loadOriginal = assetType == AssetType.image && AppSetting.get(Setting.loadOriginal);
|
final loadOriginal = assetType == AssetType.image && AppSetting.get(Setting.loadOriginal);
|
||||||
yield* loadRequest(previewRequest, decode, evictOnError: !loadOriginal);
|
yield* loadRequest(previewRequest, decode, isFinal: !loadOriginal);
|
||||||
|
|
||||||
if (!loadOriginal) {
|
if (!loadOriginal) {
|
||||||
isFinished = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,8 +123,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
|||||||
}
|
}
|
||||||
|
|
||||||
final originalRequest = request = RemoteImageRequest(uri: getOriginalUrlForRemoteId(key.assetId));
|
final originalRequest = request = RemoteImageRequest(uri: getOriginalUrlForRemoteId(key.assetId));
|
||||||
yield* loadRequest(originalRequest, decode);
|
yield* loadRequest(originalRequest, decode, isFinal: true);
|
||||||
isFinished = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Object> _animatedCodec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* {
|
Stream<Object> _animatedCodec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* {
|
||||||
@@ -138,7 +136,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
|||||||
final previewRequest = request = RemoteImageRequest(
|
final previewRequest = request = RemoteImageRequest(
|
||||||
uri: getThumbnailUrlForRemoteId(key.assetId, type: AssetMediaSize.preview, thumbhash: key.thumbhash),
|
uri: getThumbnailUrlForRemoteId(key.assetId, type: AssetMediaSize.preview, thumbhash: key.thumbhash),
|
||||||
);
|
);
|
||||||
yield* loadRequest(previewRequest, decode, evictOnError: false);
|
yield* loadRequest(previewRequest, decode, isFinal: false);
|
||||||
|
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
@@ -146,7 +144,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
|||||||
|
|
||||||
// always try original for animated, since previews don't support animation
|
// always try original for animated, since previews don't support animation
|
||||||
final originalRequest = request = RemoteImageRequest(uri: getOriginalUrlForRemoteId(key.assetId));
|
final originalRequest = request = RemoteImageRequest(uri: getOriginalUrlForRemoteId(key.assetId));
|
||||||
final codec = await loadCodecRequest(originalRequest);
|
final codec = await loadCodecRequest(originalRequest, isFinal: true);
|
||||||
if (codec == null) {
|
if (codec == null) {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
@@ -154,7 +152,6 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
|||||||
throw StateError('Failed to load animated codec for asset ${key.assetId}');
|
throw StateError('Failed to load animated codec for asset ${key.assetId}');
|
||||||
}
|
}
|
||||||
yield codec;
|
yield codec;
|
||||||
isFinished = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class ThumbHashProvider extends CancellableImageProvider<ThumbHashProvider>
|
|||||||
|
|
||||||
Stream<ImageInfo> _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) {
|
Stream<ImageInfo> _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) {
|
||||||
final request = this.request = ThumbhashImageRequest(thumbhash: key.thumbHash);
|
final request = this.request = ThumbhashImageRequest(thumbhash: key.thumbHash);
|
||||||
return loadRequest(request, decode);
|
return loadRequest(request, decode, isFinal: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
Reference in New Issue
Block a user