mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
toggle cached image
This commit is contained in:
parent
dfd9ed988e
commit
f78b151b64
4 changed files with 45 additions and 25 deletions
|
|
@ -113,10 +113,10 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
bool get showingBottomSheet => ref.read(assetViewerProvider.select((s) => s.showingBottomSheet));
|
||||
bool get showingBottomSheet => ref.read(assetViewerProvider).showingBottomSheet;
|
||||
|
||||
Color get backgroundColor {
|
||||
final opacity = ref.read(assetViewerProvider.select((s) => s.backgroundOpacity));
|
||||
final opacity = ref.read(assetViewerProvider).backgroundOpacity;
|
||||
return Colors.black.withAlpha(opacity);
|
||||
}
|
||||
|
||||
|
|
@ -223,18 +223,17 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
void _onPageChanged(int index, PhotoViewControllerBase? controller) {
|
||||
_onAssetChanged(index);
|
||||
viewController = controller;
|
||||
initialPhotoViewState = controller?.value ?? initialPhotoViewState;
|
||||
|
||||
// If the bottom sheet is showing, we need to adjust scale the asset to
|
||||
// emulate the zoom effect
|
||||
if (showingBottomSheet) {
|
||||
initialScale = controller?.scale;
|
||||
// controller?.scale = _getScaleForBottomSheet;
|
||||
controller?.scale = _getScaleForBottomSheet;
|
||||
}
|
||||
}
|
||||
|
||||
void _onDragStart(
|
||||
_,
|
||||
BuildContext ctx,
|
||||
DragStartDetails details,
|
||||
PhotoViewControllerBase controller,
|
||||
PhotoViewScaleStateController scaleStateController,
|
||||
|
|
@ -250,7 +249,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
}
|
||||
}
|
||||
|
||||
void _onDragEnd(BuildContext ctx, _, __) {
|
||||
void _onDragEnd(BuildContext ctx, DragEndDetails details, PhotoViewControllerValue value) {
|
||||
dragInProgress = false;
|
||||
|
||||
if (shouldPopOnDrag) {
|
||||
|
|
@ -281,7 +280,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
ref.read(assetViewerProvider.notifier).setOpacity(255);
|
||||
}
|
||||
|
||||
void _onDragUpdate(BuildContext ctx, DragUpdateDetails details, _) {
|
||||
void _onDragUpdate(BuildContext ctx, DragUpdateDetails details, PhotoViewControllerValue value) {
|
||||
if (blockGestures) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -335,7 +334,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
ref.read(assetViewerProvider.notifier).setOpacity(backgroundOpacity);
|
||||
}
|
||||
|
||||
void _onTapDown(_, __, ___) {
|
||||
void _onTapDown(BuildContext ctx, TapDownDetails details, PhotoViewControllerValue value) {
|
||||
if (!showingBottomSheet) {
|
||||
ref.read(assetViewerProvider.notifier).toggleControls();
|
||||
}
|
||||
|
|
@ -431,7 +430,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
void _openBottomSheet(BuildContext ctx, {double extent = _kBottomSheetMinimumExtent}) {
|
||||
ref.read(assetViewerProvider.notifier).setBottomSheet(true);
|
||||
initialScale = viewController?.scale;
|
||||
// viewController?.updateMultiple(scale: _getScaleForBottomSheet);
|
||||
viewController?.updateMultiple(scale: _getScaleForBottomSheet);
|
||||
previousExtent = _kBottomSheetMinimumExtent;
|
||||
sheetCloseController = showBottomSheet(
|
||||
context: ctx,
|
||||
|
|
@ -450,8 +449,8 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
}
|
||||
|
||||
void _handleSheetClose() {
|
||||
// viewController?.animateMultiple(position: Offset.zero);
|
||||
// viewController?.updateMultiple(scale: initialScale);
|
||||
viewController?.animateMultiple(position: Offset.zero);
|
||||
viewController?.updateMultiple(scale: initialScale);
|
||||
ref.read(assetViewerProvider.notifier).setBottomSheet(false);
|
||||
sheetCloseController = null;
|
||||
shouldPopOnDrag = false;
|
||||
|
|
@ -472,7 +471,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
|
||||
if (stackChildren != null && stackChildren.isNotEmpty) {
|
||||
asset = stackChildren.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
|
||||
asset = stackChildren.elementAt(ref.read(assetViewerProvider).stackIndex);
|
||||
}
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
|
|
@ -488,7 +487,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
}
|
||||
}
|
||||
|
||||
void _onLongPress(_, __, ___) {
|
||||
void _onLongPress(BuildContext ctx, LongPressStartDetails details, PhotoViewControllerValue value) {
|
||||
ref.read(isPlayingMotionVideoProvider.notifier).playing = true;
|
||||
}
|
||||
|
||||
|
|
@ -497,7 +496,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
|
||||
if (stackChildren != null && stackChildren.isNotEmpty) {
|
||||
asset = stackChildren.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
|
||||
asset = stackChildren.elementAt(ref.read(assetViewerProvider).stackIndex);
|
||||
}
|
||||
|
||||
final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider);
|
||||
|
|
@ -512,7 +511,12 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
final size = ctx.sizeData;
|
||||
return PhotoViewGalleryPageOptions(
|
||||
key: ValueKey(asset.heroTag),
|
||||
imageProvider: getFullImageProvider(asset, size: size),
|
||||
// When the bottom sheet is shown and the asset is changed,
|
||||
// the cached image can have different position and scale than the normal one,
|
||||
// causing incorrect animation calculations once the image provider yields a new image.
|
||||
// This is a workaround to ensure the animation is handled correctly in this case.
|
||||
// TODO: handle this without needing to disable caching
|
||||
imageProvider: getFullImageProvider(asset, size: size, showCached: !showingBottomSheet),
|
||||
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
|
||||
filterQuality: FilterQuality.high,
|
||||
tightMode: true,
|
||||
|
|
@ -577,9 +581,11 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
Widget build(BuildContext context) {
|
||||
// Rebuild the widget when the asset viewer state changes
|
||||
// Using multiple selectors to avoid unnecessary rebuilds for other state changes
|
||||
ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
|
||||
ref.watch(assetViewerProvider.select((s) => s.backgroundOpacity));
|
||||
ref.watch(assetViewerProvider.select((s) => s.stackIndex));
|
||||
ref.watch(
|
||||
assetViewerProvider.select(
|
||||
(s) => s.showingBottomSheet.hashCode ^ s.backgroundOpacity.hashCode ^ s.stackIndex.hashCode,
|
||||
),
|
||||
);
|
||||
ref.watch(isPlayingMotionVideoProvider);
|
||||
|
||||
// Listen for casting changes and send initial asset to the cast provider
|
||||
|
|
|
|||
|
|
@ -6,12 +6,18 @@ import 'package:immich_mobile/presentation/widgets/images/local_image_provider.d
|
|||
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
|
||||
|
||||
ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080, 1920)}) {
|
||||
ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080, 1920), bool showCached = true}) {
|
||||
// Create new provider and cache it
|
||||
final ImageProvider provider;
|
||||
if (_shouldUseLocalAsset(asset)) {
|
||||
final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!;
|
||||
provider = LocalFullImageProvider(id: id, size: size, type: asset.type, updatedAt: asset.updatedAt);
|
||||
provider = LocalFullImageProvider(
|
||||
id: id,
|
||||
size: size,
|
||||
type: asset.type,
|
||||
updatedAt: asset.updatedAt,
|
||||
showCached: showCached,
|
||||
);
|
||||
} else {
|
||||
final String assetId;
|
||||
if (asset is LocalAsset && asset.hasRemote) {
|
||||
|
|
@ -21,7 +27,7 @@ ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080
|
|||
} else {
|
||||
throw ArgumentError("Unsupported asset type: ${asset.runtimeType}");
|
||||
}
|
||||
provider = RemoteFullImageProvider(assetId: assetId);
|
||||
provider = RemoteFullImageProvider(assetId: assetId, showCached: showCached);
|
||||
}
|
||||
|
||||
return provider;
|
||||
|
|
|
|||
|
|
@ -95,8 +95,15 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||
final Size size;
|
||||
final AssetType type;
|
||||
final DateTime updatedAt; // temporary, only exists to fetch cached thumbnail until local disk cache is removed
|
||||
final bool showCached;
|
||||
|
||||
const LocalFullImageProvider({required this.id, required this.size, required this.type, required this.updatedAt});
|
||||
const LocalFullImageProvider({
|
||||
required this.id,
|
||||
required this.size,
|
||||
required this.type,
|
||||
required this.updatedAt,
|
||||
this.showCached = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<LocalFullImageProvider> obtainKey(ImageConfiguration configuration) {
|
||||
|
|
@ -107,7 +114,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||
ImageStreamCompleter loadImage(LocalFullImageProvider key, ImageDecoderCallback decode) {
|
||||
return OneFramePlaceholderImageStreamCompleter(
|
||||
_codec(key, decode),
|
||||
initialImage: getCachedImage(LocalThumbProvider(id: key.id, updatedAt: key.updatedAt)),
|
||||
initialImage: showCached ? getCachedImage(LocalThumbProvider(id: key.id, updatedAt: key.updatedAt)) : null,
|
||||
informationCollector: () => <DiagnosticsNode>[
|
||||
DiagnosticsProperty<String>('Id', key.id),
|
||||
DiagnosticsProperty<DateTime>('Updated at', key.updatedAt),
|
||||
|
|
|
|||
|
|
@ -71,9 +71,10 @@ class RemoteThumbProvider extends ImageProvider<RemoteThumbProvider> {
|
|||
|
||||
class RemoteFullImageProvider extends ImageProvider<RemoteFullImageProvider> {
|
||||
final String assetId;
|
||||
final bool showCached;
|
||||
final CacheManager? cacheManager;
|
||||
|
||||
const RemoteFullImageProvider({required this.assetId, this.cacheManager});
|
||||
const RemoteFullImageProvider({required this.assetId, this.cacheManager, this.showCached = true});
|
||||
|
||||
@override
|
||||
Future<RemoteFullImageProvider> obtainKey(ImageConfiguration configuration) {
|
||||
|
|
@ -85,7 +86,7 @@ class RemoteFullImageProvider extends ImageProvider<RemoteFullImageProvider> {
|
|||
final cache = cacheManager ?? RemoteImageCacheManager();
|
||||
return OneFramePlaceholderImageStreamCompleter(
|
||||
_codec(key, cache, decode),
|
||||
initialImage: getCachedImage(RemoteThumbProvider(assetId: key.assetId)),
|
||||
initialImage: showCached ? getCachedImage(RemoteThumbProvider(assetId: key.assetId)) : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue