From 6c9e1d4837e9e385da5ca0c89c28000ad25c70d8 Mon Sep 17 00:00:00 2001
From: Samuel Newman <mozzius@protonmail.com>
Date: Tue, 24 Dec 2024 20:05:54 +0000
Subject: Unlock orientation when lightbox is open (#7257)

* unlock orientation when lightbox is open

* rm outer safe area view, make sure alt text is safe

* restore safe area view for android 14 and below

* lock orientation on launch for android

* set system ui background to black when lightbox is open

* reset state on relayout

* catch async functions with noops

* rm superfluous catches

* Delay unlock until after animation

* Simplify how key is determined

* Make landscape backdrop opaque

---------

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
---
 src/view/com/lightbox/ImageViewing/index.tsx | 39 +++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 4 deletions(-)

(limited to 'src/view/com/lightbox/ImageViewing/index.tsx')

diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx
index 96f78a709..7018d753a 100644
--- a/src/view/com/lightbox/ImageViewing/index.tsx
+++ b/src/view/com/lightbox/ImageViewing/index.tsx
@@ -40,6 +40,7 @@ import {
   useSafeAreaFrame,
   useSafeAreaInsets,
 } from 'react-native-safe-area-context'
+import * as ScreenOrientation from 'expo-screen-orientation'
 import {StatusBar} from 'expo-status-bar'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Trans} from '@lingui/macro'
@@ -60,11 +61,12 @@ import ImageItem from './components/ImageItem/ImageItem'
 
 type Rect = {x: number; y: number; width: number; height: number}
 
+const PORTRAIT_UP = ScreenOrientation.OrientationLock.PORTRAIT_UP
 const PIXEL_RATIO = PixelRatio.get()
 const EDGES =
   Platform.OS === 'android' && Platform.Version < 35
     ? (['top', 'bottom', 'left', 'right'] satisfies Edge[])
-    : (['left', 'right'] satisfies Edge[]) // iOS or Android 15+, so no top/bottom safe area
+    : ([] satisfies Edge[]) // iOS or Android 15+ bleeds into safe area
 
 const SLOW_SPRING: WithSpringConfig = {
   mass: isIOS ? 1.25 : 0.75,
@@ -102,6 +104,9 @@ export default function ImageViewRoot({
   'use no memo'
   const ref = useAnimatedRef<View>()
   const [activeLightbox, setActiveLightbox] = useState(nextLightbox)
+  const [orientation, setOrientation] = useState<'portrait' | 'landscape'>(
+    'portrait',
+  )
   const openProgress = useSharedValue(0)
 
   if (!activeLightbox && nextLightbox) {
@@ -140,6 +145,20 @@ export default function ImageViewRoot({
     },
   )
 
+  // Delay the unlock until after we've finished the scale up animation.
+  // It's complicated to do the same for locking it back so we don't attempt that.
+  useAnimatedReaction(
+    () => openProgress.get() === 1,
+    (isOpen, wasOpen) => {
+      if (isOpen && !wasOpen) {
+        runOnJS(ScreenOrientation.unlockAsync)()
+      } else if (!isOpen && wasOpen) {
+        // default is PORTRAIT_UP - set via config plugin in app.config.js -sfn
+        runOnJS(ScreenOrientation.lockAsync)(PORTRAIT_UP)
+      }
+    },
+  )
+
   const onFlyAway = React.useCallback(() => {
     'worklet'
     openProgress.set(0)
@@ -154,11 +173,21 @@ export default function ImageViewRoot({
       aria-modal
       accessibilityViewIsModal
       aria-hidden={!activeLightbox}>
-      <Animated.View ref={ref} style={{flex: 1}} collapsable={false}>
+      <Animated.View
+        ref={ref}
+        style={{flex: 1}}
+        collapsable={false}
+        onLayout={e => {
+          const layout = e.nativeEvent.layout
+          setOrientation(
+            layout.height > layout.width ? 'portrait' : 'landscape',
+          )
+        }}>
         {activeLightbox && (
           <ImageView
-            key={activeLightbox.id}
+            key={activeLightbox.id + '-' + orientation}
             lightbox={activeLightbox}
+            orientation={orientation}
             onRequestClose={onRequestClose}
             onPressSave={onPressSave}
             onPressShare={onPressShare}
@@ -174,6 +203,7 @@ export default function ImageViewRoot({
 
 function ImageView({
   lightbox,
+  orientation,
   onRequestClose,
   onPressSave,
   onPressShare,
@@ -182,6 +212,7 @@ function ImageView({
   openProgress,
 }: {
   lightbox: Lightbox
+  orientation: 'portrait' | 'landscape'
   onRequestClose: () => void
   onPressSave: (uri: string) => void
   onPressShare: (uri: string) => void
@@ -221,7 +252,7 @@ function ImageView({
     const openProgressValue = openProgress.get()
     if (openProgressValue < 1) {
       opacity = Math.sqrt(openProgressValue)
-    } else if (screenSize) {
+    } else if (screenSize && orientation === 'portrait') {
       const dragProgress = Math.min(
         Math.abs(dismissSwipeTranslateY.get()) / (screenSize.height / 2),
         1,
-- 
cgit 1.4.1

