From 361cc02a067be6fef596bfa78daefcd2d6139b1f Mon Sep 17 00:00:00 2001
From: Lenny Lin <lennylin98@gmail.com>
Date: Sun, 30 Mar 2025 11:11:45 -0500
Subject: [PATCH] lightbox: Allow zooming in with video lightbox.

Fixes #1287
---
 lib/widgets/lightbox.dart       | 29 ++++++++++++++++-------------
 test/widgets/lightbox_test.dart | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+), 13 deletions(-)

diff --git a/lib/widgets/lightbox.dart b/lib/widgets/lightbox.dart
index dd2aca5b82..1772c4e296 100644
--- a/lib/widgets/lightbox.dart
+++ b/lib/widgets/lightbox.dart
@@ -549,19 +549,22 @@ class _VideoLightboxPageState extends State<VideoLightboxPage> with PerAccountSt
       message: widget.message,
       buildAppBarBottom: (context) => null,
       buildBottomAppBar: _buildBottomAppBar,
-      child: SafeArea(
-        child: Center(
-          child: Stack(alignment: Alignment.center, children: [
-            if (_controller != null && _controller!.value.isInitialized)
-              AspectRatio(
-                aspectRatio: _controller!.value.aspectRatio,
-                child: VideoPlayer(_controller!)),
-            if (_controller == null || !_controller!.value.isInitialized || _controller!.value.isBuffering)
-              const SizedBox(
-                width: 32,
-                height: 32,
-                child: CircularProgressIndicator(color: Colors.white)),
-            ]))));
+      child: Stack(alignment: Alignment.center, children: [
+        InteractiveViewer(
+          child: SafeArea(
+            child: Center(
+              child: (_controller != null && _controller!.value.isInitialized)
+                ? AspectRatio(
+                    aspectRatio: _controller!.value.aspectRatio,
+                    child: VideoPlayer(_controller!))
+                : Container()))
+        ),
+        if (_controller == null || !_controller!.value.isInitialized || _controller!.value.isBuffering)
+          const SizedBox(
+            width: 32,
+            height: 32,
+            child: CircularProgressIndicator(color: Colors.white)),
+      ]));
   }
 }
 
diff --git a/test/widgets/lightbox_test.dart b/test/widgets/lightbox_test.dart
index 31a4132a7c..f01ac4af04 100644
--- a/test/widgets/lightbox_test.dart
+++ b/test/widgets/lightbox_test.dart
@@ -557,5 +557,38 @@ void main() {
       check(position).isGreaterThan(basePosition);
       check(platform.position).equals(position);
     });
+
+    testWidgets('video can be zoomed in and out', (tester) async {
+      await setupPage(tester, videoSrc: Uri.parse(kTestVideoUrl));
+      check(platform.isPlaying).isTrue();
+
+      final initialRect = tester.getRect(find.byType(VideoPlayer));
+      final bottomRight = initialRect.bottomRight;
+      // Define initial positions for two fingers near bottom right corner:
+      //   In the case of mismatch between media and device orientation,
+      //   the zoom gesture is still expected to work,
+      //   even if the fingers are not in the image's frame.
+      final Offset finger1Start = bottomRight + const Offset(-70.0, -70.0);
+      final Offset finger2Start = bottomRight + const Offset(-20.0, -20.0);
+      final TestGesture gesture1 = await tester.startGesture(finger1Start);
+      final TestGesture gesture2 = await tester.startGesture(finger2Start);
+      await tester.pump();
+
+      // Simulate pinch out (zoom in)
+      await gesture1.moveBy(const Offset(-20.0, -20.0));
+      await gesture2.moveBy(const Offset(20.0, 20.0));
+      await tester.pump();
+      final zoomedInRect = tester.getRect(find.byType(VideoPlayer));
+      check(zoomedInRect.width).isGreaterThan(initialRect.width);
+      check(zoomedInRect.height).isGreaterThan(initialRect.height);
+
+      // Simulate pinch out (zoom in)
+      await gesture1.moveBy(const Offset(30.0, 30.0));
+      await gesture2.moveBy(const Offset(-30.0, -30.0));
+      await tester.pump();
+      final zoomedOutRect = tester.getRect(find.byType(VideoPlayer));
+      check(zoomedOutRect.width).isLessThan(zoomedInRect.width);
+      check(zoomedOutRect.height).isLessThan(zoomedInRect.height);
+    });
   });
 }