mirror of
https://github.com/immich-app/immich.git
synced 2025-12-06 09:13:13 +03:00
Compare commits
4 Commits
187b57eca2
...
fix/mobile
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bd298a160 | ||
|
|
f78b151b64 | ||
|
|
dfd9ed988e | ||
|
|
a25f14e1b9 |
@@ -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);
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
}
|
||||
|
||||
void _onDragStart(
|
||||
_,
|
||||
BuildContext ctx,
|
||||
DragStartDetails details,
|
||||
PhotoViewControllerBase controller,
|
||||
PhotoViewScaleStateController scaleStateController,
|
||||
@@ -249,7 +249,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
}
|
||||
}
|
||||
|
||||
void _onDragEnd(BuildContext ctx, _, __) {
|
||||
void _onDragEnd(BuildContext ctx, DragEndDetails details, PhotoViewControllerValue value) {
|
||||
dragInProgress = false;
|
||||
|
||||
if (shouldPopOnDrag) {
|
||||
@@ -280,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;
|
||||
}
|
||||
@@ -334,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();
|
||||
}
|
||||
@@ -471,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,
|
||||
@@ -487,7 +487,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
}
|
||||
}
|
||||
|
||||
void _onLongPress(_, __, ___) {
|
||||
void _onLongPress(BuildContext ctx, LongPressStartDetails details, PhotoViewControllerValue value) {
|
||||
ref.read(isPlayingMotionVideoProvider.notifier).playing = true;
|
||||
}
|
||||
|
||||
@@ -496,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);
|
||||
@@ -511,12 +511,17 @@ 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,
|
||||
initialScale: PhotoViewComputedScale.contained * 0.999,
|
||||
minScale: PhotoViewComputedScale.contained * 0.999,
|
||||
initialScale: PhotoViewComputedScale.contained,
|
||||
minScale: PhotoViewComputedScale.contained,
|
||||
disableScaleGestures: showingBottomSheet,
|
||||
onDragStart: _onDragStart,
|
||||
onDragUpdate: _onDragUpdate,
|
||||
@@ -545,9 +550,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
onTapDown: _onTapDown,
|
||||
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
|
||||
filterQuality: FilterQuality.high,
|
||||
initialScale: PhotoViewComputedScale.contained * 0.99,
|
||||
initialScale: PhotoViewComputedScale.contained,
|
||||
maxScale: 1.0,
|
||||
minScale: PhotoViewComputedScale.contained * 0.99,
|
||||
minScale: PhotoViewComputedScale.contained,
|
||||
basePosition: Alignment.center,
|
||||
child: SizedBox(
|
||||
width: ctx.width,
|
||||
@@ -576,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,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user