about summary refs log tree commit diff
path: root/src/view/com/lightbox/ImageViewing/index.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/lightbox/ImageViewing/index.tsx')
-rw-r--r--src/view/com/lightbox/ImageViewing/index.tsx190
1 files changed, 82 insertions, 108 deletions
diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx
index 1a64fb3af..b6835793d 100644
--- a/src/view/com/lightbox/ImageViewing/index.tsx
+++ b/src/view/com/lightbox/ImageViewing/index.tsx
@@ -8,91 +8,72 @@
 // 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,
-  useCallback,
-  useRef,
-  useEffect,
-  useMemo,
-} from 'react'
-import {
-  Animated,
-  Dimensions,
-  StyleSheet,
-  View,
-  VirtualizedList,
-  ModalProps,
-  Platform,
-} from 'react-native'
-import {ModalsContainer} from '../../modals/Modal'
+import React, {ComponentType, useCallback, useMemo, useState} from 'react'
+import {StyleSheet, View, Platform} from 'react-native'
 
 import ImageItem from './components/ImageItem/ImageItem'
 import ImageDefaultHeader from './components/ImageDefaultHeader'
 
-import useAnimatedComponents from './hooks/useAnimatedComponents'
-import useImageIndexChange from './hooks/useImageIndexChange'
-import useRequestClose from './hooks/useRequestClose'
 import {ImageSource} from './@types'
+import Animated, {useAnimatedStyle, withSpring} from 'react-native-reanimated'
 import {Edge, SafeAreaView} from 'react-native-safe-area-context'
+import PagerView from 'react-native-pager-view'
 
 type Props = {
   images: ImageSource[]
-  keyExtractor?: (imageSrc: ImageSource, index: number) => string
-  imageIndex: number
+  initialImageIndex: number
   visible: boolean
   onRequestClose: () => void
-  onLongPress?: (image: ImageSource) => void
-  onImageIndexChange?: (imageIndex: number) => void
-  presentationStyle?: ModalProps['presentationStyle']
-  animationType?: ModalProps['animationType']
   backgroundColor?: string
-  swipeToCloseEnabled?: boolean
-  doubleTapToZoomEnabled?: boolean
-  delayLongPress?: number
   HeaderComponent?: ComponentType<{imageIndex: number}>
   FooterComponent?: ComponentType<{imageIndex: number}>
 }
 
 const DEFAULT_BG_COLOR = '#000'
-const DEFAULT_DELAY_LONG_PRESS = 800
-const SCREEN = Dimensions.get('screen')
-const SCREEN_WIDTH = SCREEN.width
 
 function ImageViewing({
   images,
-  keyExtractor,
-  imageIndex,
+  initialImageIndex,
   visible,
   onRequestClose,
-  onLongPress = () => {},
-  onImageIndexChange,
   backgroundColor = DEFAULT_BG_COLOR,
-  swipeToCloseEnabled,
-  doubleTapToZoomEnabled,
-  delayLongPress = DEFAULT_DELAY_LONG_PRESS,
   HeaderComponent,
   FooterComponent,
 }: Props) {
-  const imageList = useRef<VirtualizedList<ImageSource>>(null)
-  const [opacity, onRequestCloseEnhanced] = useRequestClose(onRequestClose)
-  const [currentImageIndex, onScroll] = useImageIndexChange(imageIndex, SCREEN)
-  const [headerTransform, footerTransform, toggleBarsVisible] =
-    useAnimatedComponents()
-
-  useEffect(() => {
-    if (onImageIndexChange) {
-      onImageIndexChange(currentImageIndex)
+  const [isScaled, setIsScaled] = useState(false)
+  const [isDragging, setIsDragging] = useState(false)
+  const [imageIndex, setImageIndex] = useState(initialImageIndex)
+  const [showControls, setShowControls] = useState(true)
+
+  const animatedHeaderStyle = useAnimatedStyle(() => ({
+    pointerEvents: showControls ? 'auto' : 'none',
+    opacity: withClampedSpring(showControls ? 1 : 0),
+    transform: [
+      {
+        translateY: withClampedSpring(showControls ? 0 : -30),
+      },
+    ],
+  }))
+  const animatedFooterStyle = useAnimatedStyle(() => ({
+    pointerEvents: showControls ? 'auto' : 'none',
+    opacity: withClampedSpring(showControls ? 1 : 0),
+    transform: [
+      {
+        translateY: withClampedSpring(showControls ? 0 : 30),
+      },
+    ],
+  }))
+
+  const onTap = useCallback(() => {
+    setShowControls(show => !show)
+  }, [])
+
+  const onZoom = useCallback((nextIsScaled: boolean) => {
+    setIsScaled(nextIsScaled)
+    if (nextIsScaled) {
+      setShowControls(false)
     }
-  }, [currentImageIndex, onImageIndexChange])
-
-  const onZoom = useCallback(
-    (isScaled: boolean) => {
-      // @ts-ignore
-      imageList?.current?.setNativeProps({scrollEnabled: !isScaled})
-      toggleBarsVisible(!isScaled)
-    },
-    [toggleBarsVisible],
-  )
+  }, [])
 
   const edges = useMemo(() => {
     if (Platform.OS === 'android') {
@@ -101,12 +82,6 @@ function ImageViewing({
     return ['left', 'right'] satisfies Edge[] // iOS, so no top/bottom safe area
   }, [])
 
-  const onLayout = useCallback(() => {
-    if (imageIndex) {
-      imageList.current?.scrollToIndex({index: imageIndex, animated: false})
-    }
-  }, [imageList, imageIndex])
-
   if (!visible) {
     return null
   }
@@ -114,60 +89,47 @@ function ImageViewing({
   return (
     <SafeAreaView
       style={styles.screen}
-      onLayout={onLayout}
       edges={edges}
       aria-modal
       accessibilityViewIsModal>
-      <ModalsContainer />
-      <View style={[styles.container, {opacity, backgroundColor}]}>
-        <Animated.View style={[styles.header, {transform: headerTransform}]}>
+      <View style={[styles.container, {backgroundColor}]}>
+        <Animated.View style={[styles.header, animatedHeaderStyle]}>
           {typeof HeaderComponent !== 'undefined' ? (
             React.createElement(HeaderComponent, {
-              imageIndex: currentImageIndex,
+              imageIndex,
             })
           ) : (
-            <ImageDefaultHeader onRequestClose={onRequestCloseEnhanced} />
+            <ImageDefaultHeader onRequestClose={onRequestClose} />
           )}
         </Animated.View>
-        <VirtualizedList
-          ref={imageList}
-          data={images}
-          horizontal
-          pagingEnabled
-          showsHorizontalScrollIndicator={false}
-          showsVerticalScrollIndicator={false}
-          getItem={(_, index) => images[index]}
-          getItemCount={() => images.length}
-          getItemLayout={(_, index) => ({
-            length: SCREEN_WIDTH,
-            offset: SCREEN_WIDTH * index,
-            index,
-          })}
-          renderItem={({item: imageSrc}) => (
-            <ImageItem
-              onZoom={onZoom}
-              imageSrc={imageSrc}
-              onRequestClose={onRequestCloseEnhanced}
-              onLongPress={onLongPress}
-              delayLongPress={delayLongPress}
-              swipeToCloseEnabled={swipeToCloseEnabled}
-              doubleTapToZoomEnabled={doubleTapToZoomEnabled}
-            />
-          )}
-          onMomentumScrollEnd={onScroll}
-          //@ts-ignore
-          keyExtractor={(imageSrc, index) =>
-            keyExtractor
-              ? keyExtractor(imageSrc, index)
-              : typeof imageSrc === 'number'
-              ? `${imageSrc}`
-              : imageSrc.uri
-          }
-        />
+        <PagerView
+          scrollEnabled={!isScaled}
+          initialPage={initialImageIndex}
+          onPageSelected={e => {
+            setImageIndex(e.nativeEvent.position)
+            setIsScaled(false)
+          }}
+          onPageScrollStateChanged={e => {
+            setIsDragging(e.nativeEvent.pageScrollState !== 'idle')
+          }}
+          overdrag={true}
+          style={styles.pager}>
+          {images.map(imageSrc => (
+            <View key={imageSrc.uri}>
+              <ImageItem
+                onTap={onTap}
+                onZoom={onZoom}
+                imageSrc={imageSrc}
+                onRequestClose={onRequestClose}
+                isScrollViewBeingDragged={isDragging}
+              />
+            </View>
+          ))}
+        </PagerView>
         {typeof FooterComponent !== 'undefined' && (
-          <Animated.View style={[styles.footer, {transform: footerTransform}]}>
+          <Animated.View style={[styles.footer, animatedFooterStyle]}>
             {React.createElement(FooterComponent, {
-              imageIndex: currentImageIndex,
+              imageIndex,
             })}
           </Animated.View>
         )}
@@ -179,11 +141,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%',
@@ -200,7 +169,12 @@ const styles = StyleSheet.create({
 })
 
 const EnhancedImageViewing = (props: Props) => (
-  <ImageViewing key={props.imageIndex} {...props} />
+  <ImageViewing key={props.initialImageIndex} {...props} />
 )
 
+function withClampedSpring(value: any) {
+  'worklet'
+  return withSpring(value, {overshootClamping: true, stiffness: 300})
+}
+
 export default EnhancedImageViewing