diff --git a/package.json b/package.json
index b816b1c4a1..40b0f800be 100644
--- a/package.json
+++ b/package.json
@@ -192,7 +192,6 @@
"react-native-get-random-values": "~1.11.0",
"react-native-keyboard-controller": "1.18.5",
"react-native-mmkv": "^2.12.2",
- "react-native-pager-view": "6.8.0",
"react-native-progress": "bluesky-social/react-native-progress",
"react-native-qrcode-styled": "^0.3.3",
"react-native-reanimated": "^3.19.1",
diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx
index bb3c39f594..6c1531ea36 100644
--- a/src/view/com/lightbox/ImageViewing/index.tsx
+++ b/src/view/com/lightbox/ImageViewing/index.tsx
@@ -8,37 +8,27 @@
// 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, {useCallback, useEffect, useMemo, useState} from 'react'
-import {LayoutAnimation, PixelRatio, StyleSheet, View} from 'react-native'
+import React, {useEffect, useMemo, useState} from 'react'
+import {LayoutAnimation, StyleSheet, View} from 'react-native'
import {SystemBars} from 'react-native-edge-to-edge'
-import {Gesture} from 'react-native-gesture-handler'
-import PagerView from 'react-native-pager-view'
import Animated, {
type AnimatedRef,
cancelAnimation,
- interpolate,
measure,
runOnJS,
type SharedValue,
useAnimatedReaction,
useAnimatedRef,
useAnimatedStyle,
- useDerivedValue,
useSharedValue,
- withDecay,
withSpring,
type WithSpringConfig,
} from 'react-native-reanimated'
-import {
- SafeAreaView,
- useSafeAreaFrame,
- useSafeAreaInsets,
-} from 'react-native-safe-area-context'
+import {SafeAreaView} from 'react-native-safe-area-context'
import * as ScreenOrientation from 'expo-screen-orientation'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {Trans} from '@lingui/macro'
-import {type Dimensions} from '#/lib/media/types'
import {colors, s} from '#/lib/styles'
import {isIOS} from '#/platform/detection'
import {type Lightbox} from '#/state/lightbox'
@@ -48,14 +38,10 @@ import {ScrollView} from '#/view/com/util/Views'
import {useTheme} from '#/alf'
import {setSystemUITheme} from '#/alf/util/systemUI'
import {PlatformInfo} from '../../../../../modules/expo-bluesky-swiss-army'
-import {type ImageSource, type Transform} from './@types'
+import {type 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 PORTRAIT_UP = ScreenOrientation.OrientationLock.PORTRAIT_UP
-const PIXEL_RATIO = PixelRatio.get()
const SLOW_SPRING: WithSpringConfig = {
mass: isIOS ? 1.25 : 0.75,
@@ -210,10 +196,9 @@ function ImageView({
}) {
const {images, index: initialImageIndex} = lightbox
const isAnimated = useMemo(() => canAnimate(lightbox), [lightbox])
- const [isScaled, setIsScaled] = useState(false)
- const [isDragging, setIsDragging] = useState(false)
- const [imageIndex, setImageIndex] = useState(initialImageIndex)
- const [showControls, setShowControls] = useState(true)
+ const [isScaled] = useState(false)
+ const [imageIndex] = useState(initialImageIndex)
+ const [showControls] = useState(true)
const [isAltExpanded, setAltExpanded] = React.useState(false)
const dismissSwipeTranslateY = useSharedValue(0)
const isFlyingAway = useSharedValue(false)
@@ -285,17 +270,6 @@ function ImageView({
}
})
- const onTap = useCallback(() => {
- setShowControls(show => !show)
- }, [])
-
- const onZoom = useCallback((nextIsScaled: boolean) => {
- setIsScaled(nextIsScaled)
- if (nextIsScaled) {
- setShowControls(false)
- }
- }, [])
-
useAnimatedReaction(
() => {
const screenSize = measure(safeAreaRef)
@@ -335,37 +309,6 @@ function ImageView({
style={[styles.backdrop, backdropStyle]}
renderToHardwareTextureAndroid
/>
- {
- setImageIndex(e.nativeEvent.position)
- setIsScaled(false)
- }}
- onPageScrollStateChanged={e => {
- setIsDragging(e.nativeEvent.pageScrollState !== 'idle')
- }}
- overdrag={true}
- style={styles.pager}>
- {images.map((imageSrc, i) => (
-
-
-
- ))}
-
void
- onTap: () => void
- onZoom: (scaled: boolean) => void
- isScrollViewBeingDragged: boolean
- isScaled: boolean
- isActive: boolean
- isFlyingAway: SharedValue
- showControls: boolean
- safeAreaRef: AnimatedRef
- openProgress: SharedValue
- dismissSwipeTranslateY: SharedValue
-}) {
- const [fetchedDims, setFetchedDims] = React.useState(null)
- const dims = fetchedDims ?? imageSrc.dimensions ?? imageSrc.thumbDimensions
- let imageAspect: number | undefined
- if (dims) {
- imageAspect = dims.width / dims.height
- if (Number.isNaN(imageAspect)) {
- imageAspect = undefined
- }
- }
-
- 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} = imageSrc
- const transforms = useDerivedValue(() => {
- 'worklet'
- const safeArea = measureSafeArea()
- const openProgressValue = openProgress.get()
- const dismissTranslateY =
- isActive && openProgressValue === 1 ? dismissSwipeTranslateY.get() : 0
-
- if (openProgressValue === 0 && isFlyingAway.get()) {
- return {
- isHidden: true,
- isResting: false,
- scaleAndMoveTransform: [],
- cropFrameTransform: [],
- cropContentTransform: [],
- }
- }
-
- if (isActive && thumbRect && imageAspect && openProgressValue < 1) {
- return interpolateTransform(
- openProgressValue,
- thumbRect,
- safeArea,
- imageAspect,
- )
- }
- return {
- isHidden: false,
- isResting: dismissTranslateY === 0,
- scaleAndMoveTransform: [{translateY: dismissTranslateY}],
- cropFrameTransform: [],
- cropContentTransform: [],
- }
- })
-
- const dismissSwipePan = Gesture.Pan()
- .enabled(isActive && !isScaled)
- .activeOffsetY([-10, 10])
- .failOffsetX([-10, 10])
- .maxPointers(1)
- .onUpdate(e => {
- 'worklet'
- if (openProgress.get() !== 1 || isFlyingAway.get()) {
- return
- }
- dismissSwipeTranslateY.set(e.translationY)
- })
- .onEnd(e => {
- 'worklet'
- if (openProgress.get() !== 1 || isFlyingAway.get()) {
- return
- }
- if (Math.abs(e.velocityY) > 200) {
- isFlyingAway.set(true)
- if (dismissSwipeTranslateY.get() === 0) {
- // HACK: If the initial value is 0, withDecay() animation doesn't start.
- // This is a bug in Reanimated, but for now we'll work around it like this.
- dismissSwipeTranslateY.set(1)
- }
- dismissSwipeTranslateY.set(() => {
- 'worklet'
- return withDecay({
- velocity: e.velocityY,
- velocityFactor: Math.max(3500 / Math.abs(e.velocityY), 1), // Speed up if it's too slow.
- deceleration: 1, // Danger! This relies on the reaction below stopping it.
- })
- })
- } else {
- dismissSwipeTranslateY.set(() => {
- 'worklet'
- return withSpring(0, {
- stiffness: 700,
- damping: 50,
- })
- })
- }
- })
-
- return (
-
- )
-}
-
function LightboxFooter({
images,
index,
@@ -698,90 +483,6 @@ const styles = StyleSheet.create({
},
})
-function interpolatePx(
- px: number,
- inputRange: readonly number[],
- outputRange: readonly number[],
-) {
- 'worklet'
- const value = interpolate(px, inputRange, outputRange)
- return Math.round(value * PIXEL_RATIO) / PIXEL_RATIO
-}
-
-function interpolateTransform(
- progress: number,
- thumbnailDims: {
- pageX: number
- width: number
- pageY: number
- height: number
- },
- safeArea: {width: number; height: number; x: number; y: number},
- imageAspect: number,
-): {
- scaleAndMoveTransform: Transform
- cropFrameTransform: Transform
- cropContentTransform: Transform
- isResting: boolean
- isHidden: boolean
-} {
- 'worklet'
- const thumbAspect = thumbnailDims.width / thumbnailDims.height
- let uncroppedInitialWidth
- let uncroppedInitialHeight
- if (imageAspect > thumbAspect) {
- uncroppedInitialWidth = thumbnailDims.height * imageAspect
- uncroppedInitialHeight = thumbnailDims.height
- } else {
- uncroppedInitialWidth = thumbnailDims.width
- uncroppedInitialHeight = thumbnailDims.width / imageAspect
- }
- const safeAreaAspect = safeArea.width / safeArea.height
- let finalWidth
- let finalHeight
- if (safeAreaAspect > imageAspect) {
- finalWidth = safeArea.height * imageAspect
- finalHeight = safeArea.height
- } else {
- finalWidth = safeArea.width
- finalHeight = safeArea.width / imageAspect
- }
- const initialScale = Math.min(
- uncroppedInitialWidth / finalWidth,
- uncroppedInitialHeight / finalHeight,
- )
- const croppedFinalWidth = thumbnailDims.width / initialScale
- const croppedFinalHeight = thumbnailDims.height / initialScale
- const screenCenterX = safeArea.width / 2
- const screenCenterY = safeArea.height / 2
- const thumbnailSafeAreaX = thumbnailDims.pageX - safeArea.x
- const thumbnailSafeAreaY = thumbnailDims.pageY - safeArea.y
- const thumbnailCenterX = thumbnailSafeAreaX + thumbnailDims.width / 2
- const thumbnailCenterY = thumbnailSafeAreaY + thumbnailDims.height / 2
- const initialTranslateX = thumbnailCenterX - screenCenterX
- const initialTranslateY = thumbnailCenterY - screenCenterY
- const scale = interpolate(progress, [0, 1], [initialScale, 1])
- const translateX = interpolatePx(progress, [0, 1], [initialTranslateX, 0])
- const translateY = interpolatePx(progress, [0, 1], [initialTranslateY, 0])
- const cropScaleX = interpolate(
- progress,
- [0, 1],
- [croppedFinalWidth / finalWidth, 1],
- )
- const cropScaleY = interpolate(
- progress,
- [0, 1],
- [croppedFinalHeight / finalHeight, 1],
- )
- return {
- isHidden: false,
- isResting: progress === 1,
- scaleAndMoveTransform: [{translateX}, {translateY}, {scale}],
- cropFrameTransform: [{scaleX: cropScaleX}, {scaleY: cropScaleY}],
- cropContentTransform: [{scaleX: 1 / cropScaleX}, {scaleY: 1 / cropScaleY}],
- }
-}
-
function withClampedSpring(value: any, config: WithSpringConfig) {
'worklet'
return withSpring(value, {...config, overshootClamping: true})
diff --git a/src/view/com/pager/Pager.tsx b/src/view/com/pager/Pager.tsx
index 6d0982ad8d..e4ba5cea44 100644
--- a/src/view/com/pager/Pager.tsx
+++ b/src/view/com/pager/Pager.tsx
@@ -1,6 +1,5 @@
import {
type JSX,
- memo,
useCallback,
useContext,
useImperativeHandle,
@@ -11,25 +10,13 @@ import {
import {View} from 'react-native'
import {DrawerGestureContext} from 'react-native-drawer-layout'
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
-import PagerView, {
- type PagerViewOnPageScrollEventData,
- type PagerViewOnPageSelectedEvent,
- type PagerViewOnPageSelectedEventData,
- type PageScrollStateChangedNativeEventData,
-} from 'react-native-pager-view'
-import Animated, {
- runOnJS,
- type SharedValue,
- useEvent,
- useHandler,
- useSharedValue,
-} from 'react-native-reanimated'
+import {type SharedValue, useSharedValue} from 'react-native-reanimated'
import {useFocusEffect} from '@react-navigation/native'
import {useSetDrawerSwipeDisabled} from '#/state/shell'
import {atoms as a, native} from '#/alf'
-export type PageSelectedEvent = PagerViewOnPageSelectedEvent
+export type PageSelectedEvent = any
export interface PagerRef {
setPage: (index: number) => void
@@ -58,23 +45,18 @@ interface Props {
testID?: string
}
-const AnimatedPagerView = Animated.createAnimatedComponent(PagerView)
-const MemoizedAnimatedPagerView = memo(AnimatedPagerView)
-
export function Pager({
ref,
children,
initialPage = 0,
renderTabBar,
- onPageSelected: parentOnPageSelected,
onTabPressed: parentOnTabPressed,
- onPageScrollStateChanged: parentOnPageScrollStateChanged,
testID,
}: React.PropsWithChildren) {
- const [selectedPage, setSelectedPage] = useState(initialPage)
- const pagerView = useRef(null)
+ const [selectedPage] = useState(initialPage)
+ const pagerView = useRef(null)
- const [isIdle, setIsIdle] = useState(true)
+ const [isIdle] = useState(true)
const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
useFocusEffect(
useCallback(() => {
@@ -92,14 +74,6 @@ export function Pager({
},
}))
- const onPageSelectedJSThread = useCallback(
- (nextPosition: number) => {
- setSelectedPage(nextPosition)
- parentOnPageSelected?.(nextPosition)
- },
- [setSelectedPage, parentOnPageSelected],
- )
-
const onTabBarSelect = useCallback(
(index: number) => {
parentOnTabPressed?.(index)
@@ -110,38 +84,6 @@ export function Pager({
const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle')
const dragProgress = useSharedValue(selectedPage)
- const didInit = useSharedValue(false)
- const handlePageScroll = usePagerHandlers(
- {
- onPageScroll(e: PagerViewOnPageScrollEventData) {
- 'worklet'
- if (didInit.get() === false) {
- // On iOS, there's a spurious scroll event with 0 position
- // even if a different page was supplied as the initial page.
- // Ignore it and wait for the first confirmed selection instead.
- return
- }
- dragProgress.set(e.offset + e.position)
- },
- onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) {
- 'worklet'
- runOnJS(setIsIdle)(e.pageScrollState === 'idle')
- if (dragState.get() === 'idle' && e.pageScrollState === 'settling') {
- // This is a programmatic scroll on Android.
- // Stay "idle" to match iOS and avoid confusing downstream code.
- return
- }
- dragState.set(e.pageScrollState)
- parentOnPageScrollStateChanged?.(e.pageScrollState)
- },
- onPageSelected(e: PagerViewOnPageSelectedEventData) {
- 'worklet'
- didInit.set(true)
- runOnJS(onPageSelectedJSThread)(e.position)
- },
- },
- [parentOnPageScrollStateChanged],
- )
return (
@@ -151,15 +93,7 @@ export function Pager({
dragProgress,
dragState,
})}
-
-
- {children}
-
-
+ {children}
)
}
@@ -177,36 +111,3 @@ function DrawerGestureRequireFail({children}: {children: React.ReactNode}) {
return {children}
}
-
-function usePagerHandlers(
- handlers: {
- onPageScroll: (e: PagerViewOnPageScrollEventData) => void
- onPageScrollStateChanged: (e: PageScrollStateChangedNativeEventData) => void
- onPageSelected: (e: PagerViewOnPageSelectedEventData) => void
- },
- dependencies: unknown[],
-) {
- const {doDependenciesDiffer} = useHandler(handlers as any, dependencies)
- const subscribeForEvents = [
- 'onPageScroll',
- 'onPageScrollStateChanged',
- 'onPageSelected',
- ]
- return useEvent(
- event => {
- 'worklet'
- const {onPageScroll, onPageScrollStateChanged, onPageSelected} = handlers
- if (event.eventName.endsWith('onPageScroll')) {
- onPageScroll(event as any as PagerViewOnPageScrollEventData)
- } else if (event.eventName.endsWith('onPageScrollStateChanged')) {
- onPageScrollStateChanged(
- event as any as PageScrollStateChangedNativeEventData,
- )
- } else if (event.eventName.endsWith('onPageSelected')) {
- onPageSelected(event as any as PagerViewOnPageSelectedEventData)
- }
- },
- subscribeForEvents,
- doDependenciesDiffer,
- )
-}
diff --git a/yarn.lock b/yarn.lock
index 84c7e2d713..6a3c39187d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -16806,11 +16806,6 @@ react-native-mmkv@^2.12.2:
resolved "https://registry.yarnpkg.com/react-native-mmkv/-/react-native-mmkv-2.12.2.tgz#4bba0f5f04e2cf222494cce3a9794ba6a4894dee"
integrity sha512-6058Aq0p57chPrUutLGe9fYoiDVDNMU2PKV+lLFUJ3GhoHvUrLdsS1PDSCLr00yqzL4WJQ7TTzH+V8cpyrNcfg==
-react-native-pager-view@6.8.0:
- version "6.8.0"
- resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.8.0.tgz#5bac05203d911bf9bf039d47db41b1313dbd1a7a"
- integrity sha512-/wgFV8nB4TLnQ6j9e3TvNrHbPF5TINMZHaXt86GOV0NSJNMVGkWguniJVKrYLm85LL8KVhRkgdh43Rdu7PvW1A==
-
react-native-progress@bluesky-social/react-native-progress:
version "5.0.0"
resolved "https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4"