mirror of
https://github.com/immich-app/immich.git
synced 2025-12-09 14:21:02 -08:00
Compare commits
4 Commits
fix/docs-f
...
fix/mobile
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bd298a160 | ||
|
|
f78b151b64 | ||
|
|
dfd9ed988e | ||
|
|
a25f14e1b9 |
@@ -113,10 +113,10 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get showingBottomSheet => ref.read(assetViewerProvider.select((s) => s.showingBottomSheet));
|
bool get showingBottomSheet => ref.read(assetViewerProvider).showingBottomSheet;
|
||||||
|
|
||||||
Color get backgroundColor {
|
Color get backgroundColor {
|
||||||
final opacity = ref.read(assetViewerProvider.select((s) => s.backgroundOpacity));
|
final opacity = ref.read(assetViewerProvider).backgroundOpacity;
|
||||||
return Colors.black.withAlpha(opacity);
|
return Colors.black.withAlpha(opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +233,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onDragStart(
|
void _onDragStart(
|
||||||
_,
|
BuildContext ctx,
|
||||||
DragStartDetails details,
|
DragStartDetails details,
|
||||||
PhotoViewControllerBase controller,
|
PhotoViewControllerBase controller,
|
||||||
PhotoViewScaleStateController scaleStateController,
|
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;
|
dragInProgress = false;
|
||||||
|
|
||||||
if (shouldPopOnDrag) {
|
if (shouldPopOnDrag) {
|
||||||
@@ -280,7 +280,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
ref.read(assetViewerProvider.notifier).setOpacity(255);
|
ref.read(assetViewerProvider.notifier).setOpacity(255);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDragUpdate(BuildContext ctx, DragUpdateDetails details, _) {
|
void _onDragUpdate(BuildContext ctx, DragUpdateDetails details, PhotoViewControllerValue value) {
|
||||||
if (blockGestures) {
|
if (blockGestures) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -334,7 +334,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
ref.read(assetViewerProvider.notifier).setOpacity(backgroundOpacity);
|
ref.read(assetViewerProvider.notifier).setOpacity(backgroundOpacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTapDown(_, __, ___) {
|
void _onTapDown(BuildContext ctx, TapDownDetails details, PhotoViewControllerValue value) {
|
||||||
if (!showingBottomSheet) {
|
if (!showingBottomSheet) {
|
||||||
ref.read(assetViewerProvider.notifier).toggleControls();
|
ref.read(assetViewerProvider.notifier).toggleControls();
|
||||||
}
|
}
|
||||||
@@ -471,7 +471,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
|
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||||
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
|
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
|
||||||
if (stackChildren != null && stackChildren.isNotEmpty) {
|
if (stackChildren != null && stackChildren.isNotEmpty) {
|
||||||
asset = stackChildren.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
|
asset = stackChildren.elementAt(ref.read(assetViewerProvider).stackIndex);
|
||||||
}
|
}
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
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;
|
ref.read(isPlayingMotionVideoProvider.notifier).playing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,7 +496,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
|
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||||
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
|
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
|
||||||
if (stackChildren != null && stackChildren.isNotEmpty) {
|
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);
|
final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider);
|
||||||
@@ -511,12 +511,17 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
final size = ctx.sizeData;
|
final size = ctx.sizeData;
|
||||||
return PhotoViewGalleryPageOptions(
|
return PhotoViewGalleryPageOptions(
|
||||||
key: ValueKey(asset.heroTag),
|
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'),
|
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
tightMode: true,
|
tightMode: true,
|
||||||
initialScale: PhotoViewComputedScale.contained * 0.999,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
minScale: PhotoViewComputedScale.contained * 0.999,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
disableScaleGestures: showingBottomSheet,
|
disableScaleGestures: showingBottomSheet,
|
||||||
onDragStart: _onDragStart,
|
onDragStart: _onDragStart,
|
||||||
onDragUpdate: _onDragUpdate,
|
onDragUpdate: _onDragUpdate,
|
||||||
@@ -545,9 +550,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
onTapDown: _onTapDown,
|
onTapDown: _onTapDown,
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
|
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
initialScale: PhotoViewComputedScale.contained * 0.99,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
maxScale: 1.0,
|
maxScale: 1.0,
|
||||||
minScale: PhotoViewComputedScale.contained * 0.99,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
basePosition: Alignment.center,
|
basePosition: Alignment.center,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: ctx.width,
|
width: ctx.width,
|
||||||
@@ -576,9 +581,11 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Rebuild the widget when the asset viewer state changes
|
// Rebuild the widget when the asset viewer state changes
|
||||||
// Using multiple selectors to avoid unnecessary rebuilds for other state changes
|
// Using multiple selectors to avoid unnecessary rebuilds for other state changes
|
||||||
ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
|
ref.watch(
|
||||||
ref.watch(assetViewerProvider.select((s) => s.backgroundOpacity));
|
assetViewerProvider.select(
|
||||||
ref.watch(assetViewerProvider.select((s) => s.stackIndex));
|
(s) => s.showingBottomSheet.hashCode ^ s.backgroundOpacity.hashCode ^ s.stackIndex.hashCode,
|
||||||
|
),
|
||||||
|
);
|
||||||
ref.watch(isPlayingMotionVideoProvider);
|
ref.watch(isPlayingMotionVideoProvider);
|
||||||
|
|
||||||
// Listen for casting changes and send initial asset to the cast provider
|
// 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/images/remote_image_provider.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/constants.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
|
// Create new provider and cache it
|
||||||
final ImageProvider provider;
|
final ImageProvider provider;
|
||||||
if (_shouldUseLocalAsset(asset)) {
|
if (_shouldUseLocalAsset(asset)) {
|
||||||
final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!;
|
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 {
|
} else {
|
||||||
final String assetId;
|
final String assetId;
|
||||||
if (asset is LocalAsset && asset.hasRemote) {
|
if (asset is LocalAsset && asset.hasRemote) {
|
||||||
@@ -21,7 +27,7 @@ ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080
|
|||||||
} else {
|
} else {
|
||||||
throw ArgumentError("Unsupported asset type: ${asset.runtimeType}");
|
throw ArgumentError("Unsupported asset type: ${asset.runtimeType}");
|
||||||
}
|
}
|
||||||
provider = RemoteFullImageProvider(assetId: assetId);
|
provider = RemoteFullImageProvider(assetId: assetId, showCached: showCached);
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider;
|
return provider;
|
||||||
|
|||||||
@@ -95,8 +95,15 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
final Size size;
|
final Size size;
|
||||||
final AssetType type;
|
final AssetType type;
|
||||||
final DateTime updatedAt; // temporary, only exists to fetch cached thumbnail until local disk cache is removed
|
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
|
@override
|
||||||
Future<LocalFullImageProvider> obtainKey(ImageConfiguration configuration) {
|
Future<LocalFullImageProvider> obtainKey(ImageConfiguration configuration) {
|
||||||
@@ -107,7 +114,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
ImageStreamCompleter loadImage(LocalFullImageProvider key, ImageDecoderCallback decode) {
|
ImageStreamCompleter loadImage(LocalFullImageProvider key, ImageDecoderCallback decode) {
|
||||||
return OneFramePlaceholderImageStreamCompleter(
|
return OneFramePlaceholderImageStreamCompleter(
|
||||||
_codec(key, decode),
|
_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>[
|
informationCollector: () => <DiagnosticsNode>[
|
||||||
DiagnosticsProperty<String>('Id', key.id),
|
DiagnosticsProperty<String>('Id', key.id),
|
||||||
DiagnosticsProperty<DateTime>('Updated at', key.updatedAt),
|
DiagnosticsProperty<DateTime>('Updated at', key.updatedAt),
|
||||||
|
|||||||
@@ -71,9 +71,10 @@ class RemoteThumbProvider extends ImageProvider<RemoteThumbProvider> {
|
|||||||
|
|
||||||
class RemoteFullImageProvider extends ImageProvider<RemoteFullImageProvider> {
|
class RemoteFullImageProvider extends ImageProvider<RemoteFullImageProvider> {
|
||||||
final String assetId;
|
final String assetId;
|
||||||
|
final bool showCached;
|
||||||
final CacheManager? cacheManager;
|
final CacheManager? cacheManager;
|
||||||
|
|
||||||
const RemoteFullImageProvider({required this.assetId, this.cacheManager});
|
const RemoteFullImageProvider({required this.assetId, this.cacheManager, this.showCached = true});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<RemoteFullImageProvider> obtainKey(ImageConfiguration configuration) {
|
Future<RemoteFullImageProvider> obtainKey(ImageConfiguration configuration) {
|
||||||
@@ -85,7 +86,7 @@ class RemoteFullImageProvider extends ImageProvider<RemoteFullImageProvider> {
|
|||||||
final cache = cacheManager ?? RemoteImageCacheManager();
|
final cache = cacheManager ?? RemoteImageCacheManager();
|
||||||
return OneFramePlaceholderImageStreamCompleter(
|
return OneFramePlaceholderImageStreamCompleter(
|
||||||
_codec(key, cache, decode),
|
_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