about summary refs log tree commit diff
path: root/src/view/com/lightbox/ImageViewing/hooks
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/lightbox/ImageViewing/hooks')
-rw-r--r--src/view/com/lightbox/ImageViewing/hooks/useAnimatedComponents.ts47
-rw-r--r--src/view/com/lightbox/ImageViewing/hooks/useDoubleTapToZoom.ts150
-rw-r--r--src/view/com/lightbox/ImageViewing/hooks/useImageDimensions.ts22
-rw-r--r--src/view/com/lightbox/ImageViewing/hooks/useImageIndexChange.ts32
-rw-r--r--src/view/com/lightbox/ImageViewing/hooks/useImagePrefetch.ts25
-rw-r--r--src/view/com/lightbox/ImageViewing/hooks/usePanResponder.ts60
-rw-r--r--src/view/com/lightbox/ImageViewing/hooks/useRequestClose.ts24
7 files changed, 46 insertions, 314 deletions
diff --git a/src/view/com/lightbox/ImageViewing/hooks/useAnimatedComponents.ts b/src/view/com/lightbox/ImageViewing/hooks/useAnimatedComponents.ts
deleted file mode 100644
index c21cd7f2c..000000000
--- a/src/view/com/lightbox/ImageViewing/hooks/useAnimatedComponents.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Copyright (c) JOB TODAY S.A. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-import {Animated} from 'react-native'
-
-const INITIAL_POSITION = {x: 0, y: 0}
-const ANIMATION_CONFIG = {
-  duration: 200,
-  useNativeDriver: true,
-}
-
-const useAnimatedComponents = () => {
-  const headerTranslate = new Animated.ValueXY(INITIAL_POSITION)
-  const footerTranslate = new Animated.ValueXY(INITIAL_POSITION)
-
-  const toggleVisible = (isVisible: boolean) => {
-    if (isVisible) {
-      Animated.parallel([
-        Animated.timing(headerTranslate.y, {...ANIMATION_CONFIG, toValue: 0}),
-        Animated.timing(footerTranslate.y, {...ANIMATION_CONFIG, toValue: 0}),
-      ]).start()
-    } else {
-      Animated.parallel([
-        Animated.timing(headerTranslate.y, {
-          ...ANIMATION_CONFIG,
-          toValue: -300,
-        }),
-        Animated.timing(footerTranslate.y, {
-          ...ANIMATION_CONFIG,
-          toValue: 300,
-        }),
-      ]).start()
-    }
-  }
-
-  const headerTransform = headerTranslate.getTranslateTransform()
-  const footerTransform = footerTranslate.getTranslateTransform()
-
-  return [headerTransform, footerTransform, toggleVisible] as const
-}
-
-export default useAnimatedComponents
diff --git a/src/view/com/lightbox/ImageViewing/hooks/useDoubleTapToZoom.ts b/src/view/com/lightbox/ImageViewing/hooks/useDoubleTapToZoom.ts
deleted file mode 100644
index ea81d9f1c..000000000
--- a/src/view/com/lightbox/ImageViewing/hooks/useDoubleTapToZoom.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-/**
- * Copyright (c) JOB TODAY S.A. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-import React, {useCallback} from 'react'
-import {ScrollView, NativeTouchEvent, NativeSyntheticEvent} from 'react-native'
-
-import {Dimensions} from '../@types'
-
-const DOUBLE_TAP_DELAY = 300
-const MIN_ZOOM = 2
-
-let lastTapTS: number | null = null
-
-/**
- * This is iOS only.
- * Same functionality for Android implemented inside usePanResponder hook.
- */
-function useDoubleTapToZoom(
-  scrollViewRef: React.RefObject<ScrollView>,
-  scaled: boolean,
-  screen: Dimensions,
-  imageDimensions: Dimensions | null,
-) {
-  const handleDoubleTap = useCallback(
-    (event: NativeSyntheticEvent<NativeTouchEvent>) => {
-      const nowTS = new Date().getTime()
-      const scrollResponderRef = scrollViewRef?.current?.getScrollResponder()
-
-      const getZoomRectAfterDoubleTap = (
-        touchX: number,
-        touchY: number,
-      ): {
-        x: number
-        y: number
-        width: number
-        height: number
-      } => {
-        if (!imageDimensions) {
-          return {
-            x: 0,
-            y: 0,
-            width: screen.width,
-            height: screen.height,
-          }
-        }
-
-        // First, let's figure out how much we want to zoom in.
-        // We want to try to zoom in at least close enough to get rid of black bars.
-        const imageAspect = imageDimensions.width / imageDimensions.height
-        const screenAspect = screen.width / screen.height
-        const zoom = Math.max(
-          imageAspect / screenAspect,
-          screenAspect / imageAspect,
-          MIN_ZOOM,
-        )
-        // Unlike in the Android version, we don't constrain the *max* zoom level here.
-        // Instead, this is done in the ScrollView props so that it constraints pinch too.
-
-        // Next, we'll be calculating the rectangle to "zoom into" in screen coordinates.
-        // We already know the zoom level, so this gives us the rectangle size.
-        let rectWidth = screen.width / zoom
-        let rectHeight = screen.height / zoom
-
-        // Before we settle on the zoomed rect, figure out the safe area it has to be inside.
-        // We don't want to introduce new black bars or make existing black bars unbalanced.
-        let minX = 0
-        let minY = 0
-        let maxX = screen.width - rectWidth
-        let maxY = screen.height - rectHeight
-        if (imageAspect >= screenAspect) {
-          // The image has horizontal black bars. Exclude them from the safe area.
-          const renderedHeight = screen.width / imageAspect
-          const horizontalBarHeight = (screen.height - renderedHeight) / 2
-          minY += horizontalBarHeight
-          maxY -= horizontalBarHeight
-        } else {
-          // The image has vertical black bars. Exclude them from the safe area.
-          const renderedWidth = screen.height * imageAspect
-          const verticalBarWidth = (screen.width - renderedWidth) / 2
-          minX += verticalBarWidth
-          maxX -= verticalBarWidth
-        }
-
-        // Finally, we can position the rect according to its size and the safe area.
-        let rectX
-        if (maxX >= minX) {
-          // Content fills the screen horizontally so we have horizontal wiggle room.
-          // Try to keep the tapped point under the finger after zoom.
-          rectX = touchX - touchX / zoom
-          rectX = Math.min(rectX, maxX)
-          rectX = Math.max(rectX, minX)
-        } else {
-          // Keep the rect centered on the screen so that black bars are balanced.
-          rectX = screen.width / 2 - rectWidth / 2
-        }
-        let rectY
-        if (maxY >= minY) {
-          // Content fills the screen vertically so we have vertical wiggle room.
-          // Try to keep the tapped point under the finger after zoom.
-          rectY = touchY - touchY / zoom
-          rectY = Math.min(rectY, maxY)
-          rectY = Math.max(rectY, minY)
-        } else {
-          // Keep the rect centered on the screen so that black bars are balanced.
-          rectY = screen.height / 2 - rectHeight / 2
-        }
-
-        return {
-          x: rectX,
-          y: rectY,
-          height: rectHeight,
-          width: rectWidth,
-        }
-      }
-
-      if (lastTapTS && nowTS - lastTapTS < DOUBLE_TAP_DELAY) {
-        let nextZoomRect = {
-          x: 0,
-          y: 0,
-          width: screen.width,
-          height: screen.height,
-        }
-
-        const willZoom = !scaled
-        if (willZoom) {
-          const {pageX, pageY} = event.nativeEvent
-          nextZoomRect = getZoomRectAfterDoubleTap(pageX, pageY)
-        }
-
-        // @ts-ignore
-        scrollResponderRef?.scrollResponderZoomTo({
-          ...nextZoomRect, // This rect is in screen coordinates
-          animated: true,
-        })
-      } else {
-        lastTapTS = nowTS
-      }
-    },
-    [imageDimensions, scaled, screen.height, screen.width, scrollViewRef],
-  )
-
-  return handleDoubleTap
-}
-
-export default useDoubleTapToZoom
diff --git a/src/view/com/lightbox/ImageViewing/hooks/useImageDimensions.ts b/src/view/com/lightbox/ImageViewing/hooks/useImageDimensions.ts
index a5b0b6bd4..7f0851af3 100644
--- a/src/view/com/lightbox/ImageViewing/hooks/useImageDimensions.ts
+++ b/src/view/com/lightbox/ImageViewing/hooks/useImageDimensions.ts
@@ -8,11 +8,29 @@
 
 import {useEffect, useState} from 'react'
 import {Image, ImageURISource} from 'react-native'
-
-import {createCache} from '../utils'
 import {Dimensions, ImageSource} from '../@types'
 
 const CACHE_SIZE = 50
+
+type CacheStorageItem = {key: string; value: any}
+
+const createCache = (cacheSize: number) => ({
+  _storage: [] as CacheStorageItem[],
+  get(key: string): any {
+    const {value} =
+      this._storage.find(({key: storageKey}) => storageKey === key) || {}
+
+    return value
+  },
+  set(key: string, value: any) {
+    if (this._storage.length >= cacheSize) {
+      this._storage.shift()
+    }
+
+    this._storage.push({key, value})
+  },
+})
+
 const imageDimensionsCache = createCache(CACHE_SIZE)
 
 const useImageDimensions = (image: ImageSource): Dimensions | null => {
diff --git a/src/view/com/lightbox/ImageViewing/hooks/useImageIndexChange.ts b/src/view/com/lightbox/ImageViewing/hooks/useImageIndexChange.ts
deleted file mode 100644
index 16430f3aa..000000000
--- a/src/view/com/lightbox/ImageViewing/hooks/useImageIndexChange.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) JOB TODAY S.A. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-import {useState} from 'react'
-import {NativeSyntheticEvent, NativeScrollEvent} from 'react-native'
-
-import {Dimensions} from '../@types'
-
-const useImageIndexChange = (imageIndex: number, screen: Dimensions) => {
-  const [currentImageIndex, setImageIndex] = useState(imageIndex)
-  const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
-    const {
-      nativeEvent: {
-        contentOffset: {x: scrollX},
-      },
-    } = event
-
-    if (screen.width) {
-      const nextIndex = Math.round(scrollX / screen.width)
-      setImageIndex(nextIndex < 0 ? 0 : nextIndex)
-    }
-  }
-
-  return [currentImageIndex, onScroll] as const
-}
-
-export default useImageIndexChange
diff --git a/src/view/com/lightbox/ImageViewing/hooks/useImagePrefetch.ts b/src/view/com/lightbox/ImageViewing/hooks/useImagePrefetch.ts
deleted file mode 100644
index 3969945bb..000000000
--- a/src/view/com/lightbox/ImageViewing/hooks/useImagePrefetch.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * Copyright (c) JOB TODAY S.A. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-import {useEffect} from 'react'
-import {Image} from 'react-native'
-import {ImageSource} from '../@types'
-
-const useImagePrefetch = (images: ImageSource[]) => {
-  useEffect(() => {
-    images.forEach(image => {
-      //@ts-ignore
-      if (image.uri) {
-        //@ts-ignore
-        return Image.prefetch(image.uri)
-      }
-    })
-  }, [images])
-}
-
-export default useImagePrefetch
diff --git a/src/view/com/lightbox/ImageViewing/hooks/usePanResponder.ts b/src/view/com/lightbox/ImageViewing/hooks/usePanResponder.ts
index 7908504ea..85454e37e 100644
--- a/src/view/com/lightbox/ImageViewing/hooks/usePanResponder.ts
+++ b/src/view/com/lightbox/ImageViewing/hooks/usePanResponder.ts
@@ -18,16 +18,11 @@ import {
 } from 'react-native'
 
 import {Position} from '../@types'
-import {
-  getDistanceBetweenTouches,
-  getImageTranslate,
-  getImageDimensionsByTranslate,
-} from '../utils'
+import {getImageTranslate} from '../utils'
 
 const SCREEN = Dimensions.get('window')
 const SCREEN_WIDTH = SCREEN.width
 const SCREEN_HEIGHT = SCREEN.height
-const MIN_DIMENSION = Math.min(SCREEN_WIDTH, SCREEN_HEIGHT)
 const ANDROID_BAR_HEIGHT = 24
 
 const MIN_ZOOM = 2
@@ -39,18 +34,12 @@ type Props = {
   initialScale: number
   initialTranslate: Position
   onZoom: (isZoomed: boolean) => void
-  doubleTapToZoomEnabled: boolean
-  onLongPress: () => void
-  delayLongPress: number
 }
 
 const usePanResponder = ({
   initialScale,
   initialTranslate,
   onZoom,
-  doubleTapToZoomEnabled,
-  onLongPress,
-  delayLongPress,
 }: Props): Readonly<
   [GestureResponderHandlers, Animated.Value, Animated.ValueXY]
 > => {
@@ -62,9 +51,9 @@ const usePanResponder = ({
   let tmpTranslate: Position | null = null
   let isDoubleTapPerformed = false
   let lastTapTS: number | null = null
-  let longPressHandlerRef: NodeJS.Timeout | null = null
 
-  const meaningfulShift = MIN_DIMENSION * 0.01
+  // TODO: It's not valid to reinitialize Animated values during render.
+  // This is a bug.
   const scaleValue = new Animated.Value(initialScale)
   const translateValue = new Animated.ValueXY(initialTranslate)
 
@@ -155,10 +144,6 @@ const usePanResponder = ({
     return () => scaleValue.removeAllListeners()
   })
 
-  const cancelLongPressHandle = () => {
-    longPressHandlerRef && clearTimeout(longPressHandlerRef)
-  }
-
   const panResponder = PanResponder.create({
     onStartShouldSetPanResponder: () => true,
     onStartShouldSetPanResponderCapture: () => true,
@@ -173,8 +158,6 @@ const usePanResponder = ({
       if (gestureState.numberActiveTouches > 1) {
         return
       }
-
-      longPressHandlerRef = setTimeout(onLongPress, delayLongPress)
     },
     onPanResponderStart: (
       event: GestureResponderEvent,
@@ -194,7 +177,7 @@ const usePanResponder = ({
         lastTapTS && tapTS - lastTapTS < DOUBLE_TAP_DELAY,
       )
 
-      if (doubleTapToZoomEnabled && isDoubleTapPerformed) {
+      if (isDoubleTapPerformed) {
         let nextScale = initialScale
         let nextTranslate = initialTranslate
 
@@ -241,15 +224,8 @@ const usePanResponder = ({
       event: GestureResponderEvent,
       gestureState: PanResponderGestureState,
     ) => {
-      const {dx, dy} = gestureState
-
-      if (Math.abs(dx) >= meaningfulShift || Math.abs(dy) >= meaningfulShift) {
-        cancelLongPressHandle()
-      }
-
       // Don't need to handle move because double tap in progress (was handled in onStart)
-      if (doubleTapToZoomEnabled && isDoubleTapPerformed) {
-        cancelLongPressHandle()
+      if (isDoubleTapPerformed) {
         return
       }
 
@@ -267,8 +243,6 @@ const usePanResponder = ({
         numberInitialTouches === 2 && gestureState.numberActiveTouches === 2
 
       if (isPinchGesture) {
-        cancelLongPressHandle()
-
         const initialDistance = getDistanceBetweenTouches(initialTouches)
         const currentDistance = getDistanceBetweenTouches(
           event.nativeEvent.touches,
@@ -315,7 +289,7 @@ const usePanResponder = ({
 
       if (isTapGesture && currentScale > initialScale) {
         const {x, y} = currentTranslate
-        // eslint-disable-next-line @typescript-eslint/no-shadow
+
         const {dx, dy} = gestureState
         const [topBound, leftBound, bottomBound, rightBound] =
           getBounds(currentScale)
@@ -360,8 +334,6 @@ const usePanResponder = ({
       }
     },
     onPanResponderRelease: () => {
-      cancelLongPressHandle()
-
       if (isDoubleTapPerformed) {
         isDoubleTapPerformed = false
       }
@@ -428,4 +400,24 @@ const usePanResponder = ({
   return [panResponder.panHandlers, scaleValue, translateValue]
 }
 
+const getImageDimensionsByTranslate = (
+  translate: Position,
+  screen: {width: number; height: number},
+): {width: number; height: number} => ({
+  width: screen.width - translate.x * 2,
+  height: screen.height - translate.y * 2,
+})
+
+const getDistanceBetweenTouches = (touches: NativeTouchEvent[]): number => {
+  const [a, b] = touches
+
+  if (a == null || b == null) {
+    return 0
+  }
+
+  return Math.sqrt(
+    Math.pow(a.pageX - b.pageX, 2) + Math.pow(a.pageY - b.pageY, 2),
+  )
+}
+
 export default usePanResponder
diff --git a/src/view/com/lightbox/ImageViewing/hooks/useRequestClose.ts b/src/view/com/lightbox/ImageViewing/hooks/useRequestClose.ts
deleted file mode 100644
index 4cd03fe71..000000000
--- a/src/view/com/lightbox/ImageViewing/hooks/useRequestClose.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (c) JOB TODAY S.A. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-import {useState} from 'react'
-
-const useRequestClose = (onRequestClose: () => void) => {
-  const [opacity, setOpacity] = useState(1)
-
-  return [
-    opacity,
-    () => {
-      setOpacity(0)
-      onRequestClose()
-      setTimeout(() => setOpacity(1), 0)
-    },
-  ] as const
-}
-
-export default useRequestClose