diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx index 8f9848fbf7..3645983e6c 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx @@ -1,13 +1,11 @@ import React, {useState} from 'react' -import {ActivityIndicator, StyleProp, StyleSheet, View} from 'react-native' +import {ActivityIndicator, StyleProp, StyleSheet} from 'react-native' import { Gesture, GestureDetector, PanGesture, } from 'react-native-gesture-handler' import Animated, { - AnimatedRef, - measure, runOnJS, useAnimatedReaction, useAnimatedRef, @@ -42,7 +40,12 @@ type Props = { onZoom: (isZoomed: boolean) => void isScrollViewBeingDragged: boolean showControls: boolean - safeAreaRef: AnimatedRef + measureSafeArea: () => { + x: number + y: number + width: number + height: number + } imageAspect: number | undefined imageDimensions: ImageDimensions | undefined imageStyle: StyleProp @@ -53,7 +56,7 @@ const ImageItem = ({ onTap, onZoom, isScrollViewBeingDragged, - safeAreaRef, + measureSafeArea, imageAspect, imageDimensions, imageStyle, @@ -143,10 +146,7 @@ const ImageItem = ({ const pinch = Gesture.Pinch() .onStart(e => { 'worklet' - const screenSize = measure(safeAreaRef) - if (!screenSize) { - return - } + const screenSize = measureSafeArea() pinchOrigin.value = { x: e.focalX - screenSize.width / 2, y: e.focalY - screenSize.height / 2, @@ -154,8 +154,8 @@ const ImageItem = ({ }) .onChange(e => { 'worklet' - const screenSize = measure(safeAreaRef) - if (!imageDimensions || !screenSize) { + const screenSize = measureSafeArea() + if (!imageDimensions) { return } // Don't let the picture zoom in so close that it gets blurry. @@ -213,8 +213,8 @@ const ImageItem = ({ .minPointers(isScaled ? 1 : 2) .onChange(e => { 'worklet' - const screenSize = measure(safeAreaRef) - if (!imageDimensions || !screenSize) { + const screenSize = measureSafeArea() + if (!imageDimensions) { return } @@ -257,8 +257,8 @@ const ImageItem = ({ .numberOfTaps(2) .onEnd(e => { 'worklet' - const screenSize = measure(safeAreaRef) - if (!imageDimensions || !imageAspect || !screenSize) { + const screenSize = measureSafeArea() + if (!imageDimensions || !imageAspect) { return } const [, , committedScale] = readTransform(committedTransform.value) diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx index 8a0f43cd6d..b1fdfc7159 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx @@ -7,18 +7,13 @@ */ import React, {useState} from 'react' -import {ActivityIndicator, StyleProp, StyleSheet, View} from 'react-native' +import {ActivityIndicator, StyleProp, StyleSheet} from 'react-native' import { Gesture, GestureDetector, PanGesture, } from 'react-native-gesture-handler' -import Animated, { - AnimatedRef, - measure, - runOnJS, - useAnimatedRef, -} from 'react-native-reanimated' +import Animated, {runOnJS, useAnimatedRef} from 'react-native-reanimated' import {useSafeAreaFrame} from 'react-native-safe-area-context' import {Image, ImageStyle} from 'expo-image' @@ -37,7 +32,12 @@ type Props = { onZoom: (scaled: boolean) => void isScrollViewBeingDragged: boolean showControls: boolean - safeAreaRef: AnimatedRef + measureSafeArea: () => { + x: number + y: number + width: number + height: number + } imageAspect: number | undefined imageDimensions: ImageDimensions | undefined imageStyle: StyleProp @@ -49,7 +49,7 @@ const ImageItem = ({ onTap, onZoom, showControls, - safeAreaRef, + measureSafeArea, imageAspect, imageDimensions, imageStyle, @@ -103,10 +103,7 @@ const ImageItem = ({ .numberOfTaps(2) .onEnd(e => { 'worklet' - const screenSize = measure(safeAreaRef) - if (!screenSize) { - return - } + const screenSize = measureSafeArea() const {absoluteX, absoluteY} = e let nextZoomRect = { x: 0, diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx index 1cd6b00204..5365159b59 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx @@ -3,7 +3,6 @@ import React from 'react' import {ImageStyle, StyleProp, View} from 'react-native' import {PanGesture} from 'react-native-gesture-handler' -import {AnimatedRef} from 'react-native-reanimated' import {Dimensions as ImageDimensions, ImageSource} from '../../@types' @@ -14,7 +13,12 @@ type Props = { onZoom: (scaled: boolean) => void isScrollViewBeingDragged: boolean showControls: boolean - safeAreaRef: AnimatedRef + measureSafeArea: () => { + x: number + y: number + width: number + height: number + } imageAspect: number | undefined imageDimensions: ImageDimensions | undefined imageStyle: StyleProp diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx index b2b0601929..567f2c94c6 100644 --- a/src/view/com/lightbox/ImageViewing/index.tsx +++ b/src/view/com/lightbox/ImageViewing/index.tsx @@ -33,7 +33,12 @@ import Animated, { withDecay, withSpring, } from 'react-native-reanimated' -import {Edge, SafeAreaView} from 'react-native-safe-area-context' +import { + Edge, + SafeAreaView, + useSafeAreaFrame, + useSafeAreaInsets, +} from 'react-native-safe-area-context' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Trans} from '@lingui/macro' @@ -49,6 +54,8 @@ import {ImageSource} from './@types' import ImageDefaultHeader from './components/ImageDefaultHeader' import ImageItem from './components/ImageItem/ImageItem' +type Rect = {x: number; y: number; width: number; height: number} + const PIXEL_RATIO = PixelRatio.get() const SLOW_SPRING = {stiffness: 120} const FAST_SPRING = {stiffness: 700} @@ -340,13 +347,35 @@ function LightboxImage({ knownDimensions: imageSrc.dimensions, }) + const safeFrameDelayedForJSThreadOnly = useSafeAreaFrame() + const safeInsetsDelayedForJSThreadOnly = useSafeAreaInsets() + const measureSafeArea = React.useCallback(() => { + 'worklet' + let safeArea: Rect | null = measure(safeAreaRef) + if (!safeArea) { + if (_WORKLET) { + console.error('Expected to always be able to measure safe area.') + } + const frame = safeFrameDelayedForJSThreadOnly + const insets = safeInsetsDelayedForJSThreadOnly + safeArea = { + x: frame.x + insets.left, + y: frame.y + insets.top, + width: frame.width - insets.left - insets.right, + height: frame.height - insets.top - insets.bottom, + } + } + return safeArea + }, [ + safeFrameDelayedForJSThreadOnly, + safeInsetsDelayedForJSThreadOnly, + safeAreaRef, + ]) + const {thumbRect, dimensions} = imageSrc const interpolation = useDerivedValue(() => { 'worklet' - const safeArea = measure(safeAreaRef) - if (!safeArea) { - return {transform: [], width: 0, height: 0} - } + const safeArea = measureSafeArea() const finalWidth = safeArea.width const finalHeight = imageAspect ? safeArea.width / imageAspect : undefined if (isActive && thumbRect && dimensions && openProgress.value < 1) { @@ -431,7 +460,7 @@ function LightboxImage({ onRequestClose={onRequestClose} isScrollViewBeingDragged={isScrollViewBeingDragged} showControls={showControls} - safeAreaRef={safeAreaRef} + measureSafeArea={measureSafeArea} imageAspect={imageAspect} imageDimensions={imageDimensions} imageStyle={imageStyle}