From 209d8b683c730a14d23c7f57c3ec8c2e9daa2397 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 10 Oct 2023 22:37:28 +0100 Subject: [PATCH] Change lightbox to use Pager (#1666) * Change lightbox to use Pager * Fix crash issue on ios --------- Co-authored-by: Paul Frazee --- .../ImageItem/ImageItem.android.tsx | 49 ++----- .../components/ImageItem/ImageItem.ios.tsx | 6 +- .../components/ImageItem/ImageItem.tsx | 4 +- src/view/com/lightbox/ImageViewing/index.tsx | 128 +++++------------- 4 files changed, 46 insertions(+), 141 deletions(-) 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 553a4a2e..6fe7854b 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx @@ -1,9 +1,8 @@ -import React, {MutableRefObject, useState} from 'react' +import React, {useState} from 'react' import {ActivityIndicator, Dimensions, StyleSheet} from 'react-native' import {Image} from 'expo-image' import Animated, { - measure, runOnJS, useAnimatedRef, useAnimatedStyle, @@ -12,11 +11,7 @@ import Animated, { withDecay, withSpring, } from 'react-native-reanimated' -import { - GestureDetector, - Gesture, - GestureType, -} from 'react-native-gesture-handler' +import {GestureDetector, Gesture} from 'react-native-gesture-handler' import useImageDimensions from '../../hooks/useImageDimensions' import { createTransform, @@ -40,7 +35,6 @@ type Props = { imageSrc: ImageSource onRequestClose: () => void onZoom: (isZoomed: boolean) => void - pinchGestureRef: MutableRefObject isScrollViewBeingDragged: boolean } const ImageItem = ({ @@ -48,7 +42,6 @@ const ImageItem = ({ onZoom, onRequestClose, isScrollViewBeingDragged, - pinchGestureRef, }: Props) => { const [isScaled, setIsScaled] = useState(false) const [isLoaded, setIsLoaded] = useState(false) @@ -140,28 +133,7 @@ const ImageItem = ({ return [dx, dy] } - // This is a hack. - // We need to disallow any gestures (and let the native parent scroll view scroll) while you're scrolling it. - // However, there is no great reliable way to coordinate this yet in RGNH. - // This "fake" manual gesture handler whenever you're trying to touch something while the parent scrollview is not at rest. - const consumeHScroll = Gesture.Manual().onTouchesDown((e, manager) => { - if (isScrollViewBeingDragged) { - // Steal the gesture (and do nothing, so native ScrollView does its thing). - manager.activate() - return - } - const measurement = measure(containerRef) - if (!measurement || measurement.pageX !== 0) { - // Steal the gesture (and do nothing, so native ScrollView does its thing). - manager.activate() - return - } - // Fail this "fake" gesture so that the gestures after it can proceed. - manager.fail() - }) - const pinch = Gesture.Pinch() - .withRef(pinchGestureRef) .onStart(e => { pinchOrigin.value = { x: e.focalX - SCREEN.width / 2, @@ -318,19 +290,22 @@ const ImageItem = ({ } }) + const composedGesture = isScrollViewBeingDragged + ? // If the parent is not at rest, provide a no-op gesture. + Gesture.Manual() + : Gesture.Exclusive( + dismissSwipePan, + Gesture.Simultaneous(pinch, pan), + doubleTap, + ) + const isLoading = !isLoaded || !imageDimensions return ( {isLoading && ( )} - + void onZoom: (scaled: boolean) => void - pinchGestureRef: MutableRefObject isScrollViewBeingDragged: boolean } @@ -145,7 +143,7 @@ const ImageItem = ({imageSrc, onZoom, onRequestClose}: Props) => { accessibilityHint=""> setLoaded(true)} /> diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx index 898b00c7..35be96e4 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx @@ -1,15 +1,13 @@ // default implementation fallback for web -import React, {MutableRefObject} from 'react' +import React from 'react' import {View} from 'react-native' -import {GestureType} from 'react-native-gesture-handler' import {ImageSource} from '../../@types' type Props = { imageSrc: ImageSource onRequestClose: () => void onZoom: (scaled: boolean) => void - pinchGestureRef: MutableRefObject isScrollViewBeingDragged: boolean } diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx index 7d3f80b4..631a6ca0 100644 --- a/src/view/com/lightbox/ImageViewing/index.tsx +++ b/src/view/com/lightbox/ImageViewing/index.tsx @@ -8,32 +8,15 @@ // Original code copied and simplified from the link below as the codebase is currently not maintained: // https://github.com/jobtoday/react-native-image-viewing -import React, { - ComponentType, - createRef, - useCallback, - useRef, - useMemo, - useState, -} from 'react' -import { - Animated, - Dimensions, - NativeSyntheticEvent, - NativeScrollEvent, - StyleSheet, - View, - VirtualizedList, - ModalProps, - Platform, -} from 'react-native' +import React, {ComponentType, useMemo, useState} from 'react' +import {Animated, StyleSheet, View, ModalProps, Platform} from 'react-native' import ImageItem from './components/ImageItem/ImageItem' import ImageDefaultHeader from './components/ImageDefaultHeader' import {ImageSource} from './@types' -import {ScrollView, GestureType} from 'react-native-gesture-handler' import {Edge, SafeAreaView} from 'react-native-safe-area-context' +import PagerView from 'react-native-pager-view' type Props = { images: ImageSource[] @@ -48,8 +31,6 @@ type Props = { } const DEFAULT_BG_COLOR = '#000' -const SCREEN = Dimensions.get('screen') -const SCREEN_WIDTH = SCREEN.width const INITIAL_POSITION = {x: 0, y: 0} const ANIMATION_CONFIG = { duration: 200, @@ -65,7 +46,6 @@ function ImageViewing({ HeaderComponent, FooterComponent, }: Props) { - const imageList = useRef>(null) const [isScaled, setIsScaled] = useState(false) const [isDragging, setIsDragging] = useState(false) const [imageIndex, setImageIndex] = useState(initialImageIndex) @@ -96,19 +76,6 @@ function ImageViewing({ } } - const onScroll = (event: NativeSyntheticEvent) => { - const { - nativeEvent: { - contentOffset: {x: scrollX}, - }, - } = event - - if (SCREEN.width) { - const nextIndex = Math.round(scrollX / SCREEN.width) - setImageIndex(nextIndex < 0 ? 0 : nextIndex) - } - } - const onZoom = (nextIsScaled: boolean) => { toggleBarsVisible(!nextIsScaled) setIsScaled(false) @@ -121,26 +88,6 @@ function ImageViewing({ return ['left', 'right'] satisfies Edge[] // iOS, so no top/bottom safe area }, []) - const onLayout = useCallback(() => { - if (initialImageIndex) { - imageList.current?.scrollToIndex({ - index: initialImageIndex, - animated: false, - }) - } - }, [imageList, initialImageIndex]) - - // This is a hack. - // RNGH doesn't have an easy way to express that pinch of individual items - // should "steal" all pinches from the scroll view. So we're keeping a ref - // to all pinch gestures so that we may give them to . - const [pinchGestureRefs] = useState(new Map()) - for (let imageSrc of images) { - if (!pinchGestureRefs.get(imageSrc)) { - pinchGestureRefs.set(imageSrc, createRef()) - } - } - if (!visible) { return null } @@ -150,7 +97,6 @@ function ImageViewing({ return ( @@ -164,48 +110,29 @@ function ImageViewing({ )} - images[index]} - getItemCount={() => images.length} - getItemLayout={(_, index) => ({ - length: SCREEN_WIDTH, - offset: SCREEN_WIDTH * index, - index, - })} - renderItem={({item: imageSrc}) => ( - - )} - renderScrollComponent={props => ( - - )} - onScrollBeginDrag={() => { - setIsDragging(true) - }} - onScrollEndDrag={() => { - setIsDragging(false) - }} - onMomentumScrollEnd={e => { + { + setImageIndex(e.nativeEvent.position) setIsScaled(false) - onScroll(e) }} - keyExtractor={imageSrc => imageSrc.uri} - /> + onPageScrollStateChanged={e => { + setIsDragging(e.nativeEvent.pageScrollState !== 'idle') + }} + overdrag={true} + style={styles.pager}> + {images.map(imageSrc => ( + + + + ))} + {typeof FooterComponent !== 'undefined' && ( {React.createElement(FooterComponent, { @@ -221,11 +148,18 @@ function ImageViewing({ const styles = StyleSheet.create({ screen: { position: 'absolute', + top: 0, + left: 0, + bottom: 0, + right: 0, }, container: { flex: 1, backgroundColor: '#000', }, + pager: { + flex: 1, + }, header: { position: 'absolute', width: '100%',