diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index 4e4b7878a6..dfc5842faa 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -331,6 +331,11 @@ class GalleryViewerPage extends HookConsumerWidget { onTapDown: (_, __, ___) { ref.read(showControlsProvider.notifier).toggle(); }, + onLongPressStart: (_, __, ___) { + if (asset.livePhotoVideoId != null) { + isPlayingVideo.value = true; + } + }, imageProvider: provider, heroAttributes: PhotoViewHeroAttributes( tag: isFromDto diff --git a/mobile/lib/shared/ui/photo_view/photo_view.dart b/mobile/lib/shared/ui/photo_view/photo_view.dart index c48b8158c0..3d9de54793 100644 --- a/mobile/lib/shared/ui/photo_view/photo_view.dart +++ b/mobile/lib/shared/ui/photo_view/photo_view.dart @@ -256,6 +256,7 @@ class PhotoView extends StatefulWidget { this.onDragEnd, this.onDragUpdate, this.onScaleEnd, + this.onLongPressStart, this.customSize, this.gestureDetectorBehavior, this.tightMode, @@ -294,6 +295,7 @@ class PhotoView extends StatefulWidget { this.onDragEnd, this.onDragUpdate, this.onScaleEnd, + this.onLongPressStart, this.customSize, this.gestureDetectorBehavior, this.tightMode, @@ -401,6 +403,10 @@ class PhotoView extends StatefulWidget { /// particular location. final PhotoViewImageScaleEndCallback? onScaleEnd; + /// A pointer that might cause a tap has contacted the screen at a particular + /// location. + final PhotoViewImageLongPressStartCallback? onLongPressStart; + /// [HitTestBehavior] to be passed to the internal gesture detector. final HitTestBehavior? gestureDetectorBehavior; @@ -537,6 +543,7 @@ class _PhotoViewState extends State onDragEnd: widget.onDragEnd, onDragUpdate: widget.onDragUpdate, onScaleEnd: widget.onScaleEnd, + onLongPressStart: widget.onLongPressStart, outerSize: computedOuterSize, gestureDetectorBehavior: widget.gestureDetectorBehavior, tightMode: widget.tightMode, @@ -566,6 +573,7 @@ class _PhotoViewState extends State onDragEnd: widget.onDragEnd, onDragUpdate: widget.onDragUpdate, onScaleEnd: widget.onScaleEnd, + onLongPressStart: widget.onLongPressStart, outerSize: computedOuterSize, gestureDetectorBehavior: widget.gestureDetectorBehavior, tightMode: widget.tightMode, @@ -649,6 +657,13 @@ typedef PhotoViewImageScaleEndCallback = Function( PhotoViewControllerValue controllerValue, ); +/// A type definition for a callback when the user long press start +typedef PhotoViewImageLongPressStartCallback = Function( + BuildContext context, + LongPressStartDetails details, + PhotoViewControllerValue controllerValue, +); + /// A type definition for a callback to show a widget while the image is loading, a [ImageChunkEvent] is passed to inform progress typedef LoadingBuilder = Widget Function( BuildContext context, diff --git a/mobile/lib/shared/ui/photo_view/photo_view_gallery.dart b/mobile/lib/shared/ui/photo_view/photo_view_gallery.dart index f9a6107cac..48b2d80779 100644 --- a/mobile/lib/shared/ui/photo_view/photo_view_gallery.dart +++ b/mobile/lib/shared/ui/photo_view/photo_view_gallery.dart @@ -12,6 +12,7 @@ import 'package:immich_mobile/shared/ui/photo_view/photo_view.dart' PhotoViewImageDragEndCallback, PhotoViewImageDragUpdateCallback, PhotoViewImageScaleEndCallback, + PhotoViewImageLongPressStartCallback, ScaleStateCycle; import 'package:immich_mobile/shared/ui/photo_view/src/controller/photo_view_controller.dart'; @@ -269,6 +270,7 @@ class _PhotoViewGalleryState extends State { onDragEnd: pageOption.onDragEnd, onDragUpdate: pageOption.onDragUpdate, onScaleEnd: pageOption.onScaleEnd, + onLongPressStart: pageOption.onLongPressStart, gestureDetectorBehavior: pageOption.gestureDetectorBehavior, tightMode: pageOption.tightMode, filterQuality: pageOption.filterQuality, @@ -300,6 +302,7 @@ class _PhotoViewGalleryState extends State { onDragEnd: pageOption.onDragEnd, onDragUpdate: pageOption.onDragUpdate, onScaleEnd: pageOption.onScaleEnd, + onLongPressStart: pageOption.onLongPressStart, gestureDetectorBehavior: pageOption.gestureDetectorBehavior, tightMode: pageOption.tightMode, filterQuality: pageOption.filterQuality, @@ -347,6 +350,7 @@ class PhotoViewGalleryPageOptions { this.onDragEnd, this.onDragUpdate, this.onScaleEnd, + this.onLongPressStart, this.gestureDetectorBehavior, this.tightMode, this.filterQuality, @@ -373,6 +377,7 @@ class PhotoViewGalleryPageOptions { this.onDragEnd, this.onDragUpdate, this.onScaleEnd, + this.onLongPressStart, this.gestureDetectorBehavior, this.tightMode, this.filterQuality, @@ -431,6 +436,9 @@ class PhotoViewGalleryPageOptions { /// Mirror to [PhotoView.onScaleEnd] final PhotoViewImageScaleEndCallback? onScaleEnd; + /// Mirror to [PhotoView.onLongPressStart] + final PhotoViewImageLongPressStartCallback? onLongPressStart; + /// Mirror to [PhotoView.gestureDetectorBehavior] final HitTestBehavior? gestureDetectorBehavior; diff --git a/mobile/lib/shared/ui/photo_view/src/core/photo_view_core.dart b/mobile/lib/shared/ui/photo_view/src/core/photo_view_core.dart index e420421b5e..c2edb45e21 100644 --- a/mobile/lib/shared/ui/photo_view/src/core/photo_view_core.dart +++ b/mobile/lib/shared/ui/photo_view/src/core/photo_view_core.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/shared/ui/photo_view/photo_view.dart' PhotoViewImageDragEndCallback, PhotoViewImageDragStartCallback, PhotoViewImageDragUpdateCallback, + PhotoViewImageLongPressStartCallback, ScaleStateCycle; import 'package:immich_mobile/shared/ui/photo_view/src/controller/photo_view_controller.dart'; import 'package:immich_mobile/shared/ui/photo_view/src/controller/photo_view_controller_delegate.dart'; @@ -37,6 +38,7 @@ class PhotoViewCore extends StatefulWidget { required this.onDragEnd, required this.onDragUpdate, required this.onScaleEnd, + required this.onLongPressStart, required this.gestureDetectorBehavior, required this.controller, required this.scaleBoundaries, @@ -61,6 +63,7 @@ class PhotoViewCore extends StatefulWidget { this.onDragEnd, this.onDragUpdate, this.onScaleEnd, + this.onLongPressStart, this.gestureDetectorBehavior, required this.controller, required this.scaleBoundaries, @@ -95,6 +98,8 @@ class PhotoViewCore extends StatefulWidget { final PhotoViewImageDragEndCallback? onDragEnd; final PhotoViewImageDragUpdateCallback? onDragUpdate; + final PhotoViewImageLongPressStartCallback? onLongPressStart; + final HitTestBehavior? gestureDetectorBehavior; final bool tightMode; final bool disableGestures; @@ -373,6 +378,9 @@ class PhotoViewCoreState extends State onTapDown: widget.onTapDown != null ? (details) => widget.onTapDown!(context, details, value) : null, + onLongPressStart: widget.onLongPressStart != null + ? (details) => widget.onLongPressStart!(context, details, value) + : null, child: child, ); } else { diff --git a/mobile/lib/shared/ui/photo_view/src/core/photo_view_gesture_detector.dart b/mobile/lib/shared/ui/photo_view/src/core/photo_view_gesture_detector.dart index 743faa0e0a..2eef5e6742 100644 --- a/mobile/lib/shared/ui/photo_view/src/core/photo_view_gesture_detector.dart +++ b/mobile/lib/shared/ui/photo_view/src/core/photo_view_gesture_detector.dart @@ -16,6 +16,7 @@ class PhotoViewGestureDetector extends StatelessWidget { this.onDragStart, this.onDragEnd, this.onDragUpdate, + this.onLongPressStart, this.child, this.onTapUp, this.onTapDown, @@ -36,6 +37,8 @@ class PhotoViewGestureDetector extends StatelessWidget { final GestureTapUpCallback? onTapUp; final GestureTapDownCallback? onTapDown; + final GestureLongPressStartCallback? onLongPressStart; + final Widget? child; final HitTestBehavior? behavior; @@ -99,6 +102,13 @@ class PhotoViewGestureDetector extends StatelessWidget { }, ); + gestures[LongPressGestureRecognizer] = + GestureRecognizerFactoryWithHandlers( + () => LongPressGestureRecognizer(debugOwner: this), + (LongPressGestureRecognizer instance) { + instance.onLongPressStart = onLongPressStart; + }); + return RawGestureDetector( behavior: behavior, gestures: gestures, diff --git a/mobile/lib/shared/ui/photo_view/src/photo_view_wrappers.dart b/mobile/lib/shared/ui/photo_view/src/photo_view_wrappers.dart index b7f86f8f0c..57496f3777 100644 --- a/mobile/lib/shared/ui/photo_view/src/photo_view_wrappers.dart +++ b/mobile/lib/shared/ui/photo_view/src/photo_view_wrappers.dart @@ -28,6 +28,7 @@ class ImageWrapper extends StatefulWidget { required this.onDragEnd, required this.onDragUpdate, required this.onScaleEnd, + required this.onLongPressStart, required this.outerSize, required this.gestureDetectorBehavior, required this.tightMode, @@ -59,6 +60,7 @@ class ImageWrapper extends StatefulWidget { final PhotoViewImageDragEndCallback? onDragEnd; final PhotoViewImageDragUpdateCallback? onDragUpdate; final PhotoViewImageScaleEndCallback? onScaleEnd; + final PhotoViewImageLongPressStartCallback? onLongPressStart; final Size outerSize; final HitTestBehavior? gestureDetectorBehavior; final bool? tightMode; @@ -205,6 +207,7 @@ class _ImageWrapperState extends State { onDragEnd: widget.onDragEnd, onDragUpdate: widget.onDragUpdate, onScaleEnd: widget.onScaleEnd, + onLongPressStart: widget.onLongPressStart, gestureDetectorBehavior: widget.gestureDetectorBehavior, tightMode: widget.tightMode ?? false, filterQuality: widget.filterQuality ?? FilterQuality.none, @@ -257,6 +260,7 @@ class CustomChildWrapper extends StatelessWidget { this.onDragEnd, this.onDragUpdate, this.onScaleEnd, + this.onLongPressStart, required this.outerSize, this.gestureDetectorBehavior, required this.tightMode, @@ -287,6 +291,7 @@ class CustomChildWrapper extends StatelessWidget { final PhotoViewImageDragEndCallback? onDragEnd; final PhotoViewImageDragUpdateCallback? onDragUpdate; final PhotoViewImageScaleEndCallback? onScaleEnd; + final PhotoViewImageLongPressStartCallback? onLongPressStart; final Size outerSize; final HitTestBehavior? gestureDetectorBehavior; final bool? tightMode; @@ -320,6 +325,7 @@ class CustomChildWrapper extends StatelessWidget { onDragEnd: onDragEnd, onDragUpdate: onDragUpdate, onScaleEnd: onScaleEnd, + onLongPressStart: onLongPressStart, gestureDetectorBehavior: gestureDetectorBehavior, tightMode: tightMode ?? false, filterQuality: filterQuality ?? FilterQuality.none,