temp rm pagerview
This commit is contained in:
@@ -192,7 +192,6 @@
|
|||||||
"react-native-get-random-values": "~1.11.0",
|
"react-native-get-random-values": "~1.11.0",
|
||||||
"react-native-keyboard-controller": "1.18.5",
|
"react-native-keyboard-controller": "1.18.5",
|
||||||
"react-native-mmkv": "^2.12.2",
|
"react-native-mmkv": "^2.12.2",
|
||||||
"react-native-pager-view": "6.8.0",
|
|
||||||
"react-native-progress": "bluesky-social/react-native-progress",
|
"react-native-progress": "bluesky-social/react-native-progress",
|
||||||
"react-native-qrcode-styled": "^0.3.3",
|
"react-native-qrcode-styled": "^0.3.3",
|
||||||
"react-native-reanimated": "^3.19.1",
|
"react-native-reanimated": "^3.19.1",
|
||||||
|
|||||||
@@ -8,37 +8,27 @@
|
|||||||
// Original code copied and simplified from the link below as the codebase is currently not maintained:
|
// Original code copied and simplified from the link below as the codebase is currently not maintained:
|
||||||
// https://github.com/jobtoday/react-native-image-viewing
|
// https://github.com/jobtoday/react-native-image-viewing
|
||||||
|
|
||||||
import React, {useCallback, useEffect, useMemo, useState} from 'react'
|
import React, {useEffect, useMemo, useState} from 'react'
|
||||||
import {LayoutAnimation, PixelRatio, StyleSheet, View} from 'react-native'
|
import {LayoutAnimation, StyleSheet, View} from 'react-native'
|
||||||
import {SystemBars} from 'react-native-edge-to-edge'
|
import {SystemBars} from 'react-native-edge-to-edge'
|
||||||
import {Gesture} from 'react-native-gesture-handler'
|
|
||||||
import PagerView from 'react-native-pager-view'
|
|
||||||
import Animated, {
|
import Animated, {
|
||||||
type AnimatedRef,
|
type AnimatedRef,
|
||||||
cancelAnimation,
|
cancelAnimation,
|
||||||
interpolate,
|
|
||||||
measure,
|
measure,
|
||||||
runOnJS,
|
runOnJS,
|
||||||
type SharedValue,
|
type SharedValue,
|
||||||
useAnimatedReaction,
|
useAnimatedReaction,
|
||||||
useAnimatedRef,
|
useAnimatedRef,
|
||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
useDerivedValue,
|
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
withDecay,
|
|
||||||
withSpring,
|
withSpring,
|
||||||
type WithSpringConfig,
|
type WithSpringConfig,
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {
|
import {SafeAreaView} from 'react-native-safe-area-context'
|
||||||
SafeAreaView,
|
|
||||||
useSafeAreaFrame,
|
|
||||||
useSafeAreaInsets,
|
|
||||||
} from 'react-native-safe-area-context'
|
|
||||||
import * as ScreenOrientation from 'expo-screen-orientation'
|
import * as ScreenOrientation from 'expo-screen-orientation'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Trans} from '@lingui/macro'
|
import {Trans} from '@lingui/macro'
|
||||||
|
|
||||||
import {type Dimensions} from '#/lib/media/types'
|
|
||||||
import {colors, s} from '#/lib/styles'
|
import {colors, s} from '#/lib/styles'
|
||||||
import {isIOS} from '#/platform/detection'
|
import {isIOS} from '#/platform/detection'
|
||||||
import {type Lightbox} from '#/state/lightbox'
|
import {type Lightbox} from '#/state/lightbox'
|
||||||
@@ -48,14 +38,10 @@ import {ScrollView} from '#/view/com/util/Views'
|
|||||||
import {useTheme} from '#/alf'
|
import {useTheme} from '#/alf'
|
||||||
import {setSystemUITheme} from '#/alf/util/systemUI'
|
import {setSystemUITheme} from '#/alf/util/systemUI'
|
||||||
import {PlatformInfo} from '../../../../../modules/expo-bluesky-swiss-army'
|
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 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 PORTRAIT_UP = ScreenOrientation.OrientationLock.PORTRAIT_UP
|
||||||
const PIXEL_RATIO = PixelRatio.get()
|
|
||||||
|
|
||||||
const SLOW_SPRING: WithSpringConfig = {
|
const SLOW_SPRING: WithSpringConfig = {
|
||||||
mass: isIOS ? 1.25 : 0.75,
|
mass: isIOS ? 1.25 : 0.75,
|
||||||
@@ -210,10 +196,9 @@ function ImageView({
|
|||||||
}) {
|
}) {
|
||||||
const {images, index: initialImageIndex} = lightbox
|
const {images, index: initialImageIndex} = lightbox
|
||||||
const isAnimated = useMemo(() => canAnimate(lightbox), [lightbox])
|
const isAnimated = useMemo(() => canAnimate(lightbox), [lightbox])
|
||||||
const [isScaled, setIsScaled] = useState(false)
|
const [isScaled] = useState(false)
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
const [imageIndex] = useState(initialImageIndex)
|
||||||
const [imageIndex, setImageIndex] = useState(initialImageIndex)
|
const [showControls] = useState(true)
|
||||||
const [showControls, setShowControls] = useState(true)
|
|
||||||
const [isAltExpanded, setAltExpanded] = React.useState(false)
|
const [isAltExpanded, setAltExpanded] = React.useState(false)
|
||||||
const dismissSwipeTranslateY = useSharedValue(0)
|
const dismissSwipeTranslateY = useSharedValue(0)
|
||||||
const isFlyingAway = useSharedValue(false)
|
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(
|
useAnimatedReaction(
|
||||||
() => {
|
() => {
|
||||||
const screenSize = measure(safeAreaRef)
|
const screenSize = measure(safeAreaRef)
|
||||||
@@ -335,37 +309,6 @@ function ImageView({
|
|||||||
style={[styles.backdrop, backdropStyle]}
|
style={[styles.backdrop, backdropStyle]}
|
||||||
renderToHardwareTextureAndroid
|
renderToHardwareTextureAndroid
|
||||||
/>
|
/>
|
||||||
<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, i) => (
|
|
||||||
<View key={imageSrc.uri}>
|
|
||||||
<LightboxImage
|
|
||||||
onTap={onTap}
|
|
||||||
onZoom={onZoom}
|
|
||||||
imageSrc={imageSrc}
|
|
||||||
onRequestClose={onRequestClose}
|
|
||||||
isScrollViewBeingDragged={isDragging}
|
|
||||||
showControls={showControls}
|
|
||||||
safeAreaRef={safeAreaRef}
|
|
||||||
isScaled={isScaled}
|
|
||||||
isFlyingAway={isFlyingAway}
|
|
||||||
isActive={i === imageIndex}
|
|
||||||
dismissSwipeTranslateY={dismissSwipeTranslateY}
|
|
||||||
openProgress={openProgress}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</PagerView>
|
|
||||||
<View style={styles.controls}>
|
<View style={styles.controls}>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={animatedHeaderStyle}
|
style={animatedHeaderStyle}
|
||||||
@@ -389,164 +332,6 @@ function ImageView({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function LightboxImage({
|
|
||||||
imageSrc,
|
|
||||||
onTap,
|
|
||||||
onZoom,
|
|
||||||
onRequestClose,
|
|
||||||
isScrollViewBeingDragged,
|
|
||||||
isScaled,
|
|
||||||
isFlyingAway,
|
|
||||||
isActive,
|
|
||||||
showControls,
|
|
||||||
safeAreaRef,
|
|
||||||
openProgress,
|
|
||||||
dismissSwipeTranslateY,
|
|
||||||
}: {
|
|
||||||
imageSrc: ImageSource
|
|
||||||
onRequestClose: () => void
|
|
||||||
onTap: () => void
|
|
||||||
onZoom: (scaled: boolean) => void
|
|
||||||
isScrollViewBeingDragged: boolean
|
|
||||||
isScaled: boolean
|
|
||||||
isActive: boolean
|
|
||||||
isFlyingAway: SharedValue<boolean>
|
|
||||||
showControls: boolean
|
|
||||||
safeAreaRef: AnimatedRef<View>
|
|
||||||
openProgress: SharedValue<number>
|
|
||||||
dismissSwipeTranslateY: SharedValue<number>
|
|
||||||
}) {
|
|
||||||
const [fetchedDims, setFetchedDims] = React.useState<Dimensions | null>(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 (
|
|
||||||
<ImageItem
|
|
||||||
imageSrc={imageSrc}
|
|
||||||
onTap={onTap}
|
|
||||||
onZoom={onZoom}
|
|
||||||
onRequestClose={onRequestClose}
|
|
||||||
onLoad={setFetchedDims}
|
|
||||||
isScrollViewBeingDragged={isScrollViewBeingDragged}
|
|
||||||
showControls={showControls}
|
|
||||||
measureSafeArea={measureSafeArea}
|
|
||||||
imageAspect={imageAspect}
|
|
||||||
imageDimensions={dims ?? undefined}
|
|
||||||
dismissSwipePan={dismissSwipePan}
|
|
||||||
transforms={transforms}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function LightboxFooter({
|
function LightboxFooter({
|
||||||
images,
|
images,
|
||||||
index,
|
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) {
|
function withClampedSpring(value: any, config: WithSpringConfig) {
|
||||||
'worklet'
|
'worklet'
|
||||||
return withSpring(value, {...config, overshootClamping: true})
|
return withSpring(value, {...config, overshootClamping: true})
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
type JSX,
|
type JSX,
|
||||||
memo,
|
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
@@ -11,25 +10,13 @@ import {
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {DrawerGestureContext} from 'react-native-drawer-layout'
|
import {DrawerGestureContext} from 'react-native-drawer-layout'
|
||||||
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
|
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
|
||||||
import PagerView, {
|
import {type SharedValue, useSharedValue} from 'react-native-reanimated'
|
||||||
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 {useFocusEffect} from '@react-navigation/native'
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
|
|
||||||
import {useSetDrawerSwipeDisabled} from '#/state/shell'
|
import {useSetDrawerSwipeDisabled} from '#/state/shell'
|
||||||
import {atoms as a, native} from '#/alf'
|
import {atoms as a, native} from '#/alf'
|
||||||
|
|
||||||
export type PageSelectedEvent = PagerViewOnPageSelectedEvent
|
export type PageSelectedEvent = any
|
||||||
|
|
||||||
export interface PagerRef {
|
export interface PagerRef {
|
||||||
setPage: (index: number) => void
|
setPage: (index: number) => void
|
||||||
@@ -58,23 +45,18 @@ interface Props {
|
|||||||
testID?: string
|
testID?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimatedPagerView = Animated.createAnimatedComponent(PagerView)
|
|
||||||
const MemoizedAnimatedPagerView = memo(AnimatedPagerView)
|
|
||||||
|
|
||||||
export function Pager({
|
export function Pager({
|
||||||
ref,
|
ref,
|
||||||
children,
|
children,
|
||||||
initialPage = 0,
|
initialPage = 0,
|
||||||
renderTabBar,
|
renderTabBar,
|
||||||
onPageSelected: parentOnPageSelected,
|
|
||||||
onTabPressed: parentOnTabPressed,
|
onTabPressed: parentOnTabPressed,
|
||||||
onPageScrollStateChanged: parentOnPageScrollStateChanged,
|
|
||||||
testID,
|
testID,
|
||||||
}: React.PropsWithChildren<Props>) {
|
}: React.PropsWithChildren<Props>) {
|
||||||
const [selectedPage, setSelectedPage] = useState(initialPage)
|
const [selectedPage] = useState(initialPage)
|
||||||
const pagerView = useRef<PagerView>(null)
|
const pagerView = useRef<any>(null)
|
||||||
|
|
||||||
const [isIdle, setIsIdle] = useState(true)
|
const [isIdle] = useState(true)
|
||||||
const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
|
const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
@@ -92,14 +74,6 @@ export function Pager({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const onPageSelectedJSThread = useCallback(
|
|
||||||
(nextPosition: number) => {
|
|
||||||
setSelectedPage(nextPosition)
|
|
||||||
parentOnPageSelected?.(nextPosition)
|
|
||||||
},
|
|
||||||
[setSelectedPage, parentOnPageSelected],
|
|
||||||
)
|
|
||||||
|
|
||||||
const onTabBarSelect = useCallback(
|
const onTabBarSelect = useCallback(
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
parentOnTabPressed?.(index)
|
parentOnTabPressed?.(index)
|
||||||
@@ -110,38 +84,6 @@ export function Pager({
|
|||||||
|
|
||||||
const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle')
|
const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle')
|
||||||
const dragProgress = useSharedValue(selectedPage)
|
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 (
|
return (
|
||||||
<View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}>
|
<View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}>
|
||||||
@@ -151,15 +93,7 @@ export function Pager({
|
|||||||
dragProgress,
|
dragProgress,
|
||||||
dragState,
|
dragState,
|
||||||
})}
|
})}
|
||||||
<DrawerGestureRequireFail>
|
<DrawerGestureRequireFail>{children}</DrawerGestureRequireFail>
|
||||||
<MemoizedAnimatedPagerView
|
|
||||||
ref={pagerView}
|
|
||||||
style={a.flex_1}
|
|
||||||
initialPage={initialPage}
|
|
||||||
onPageScroll={handlePageScroll}>
|
|
||||||
{children}
|
|
||||||
</MemoizedAnimatedPagerView>
|
|
||||||
</DrawerGestureRequireFail>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -177,36 +111,3 @@ function DrawerGestureRequireFail({children}: {children: React.ReactNode}) {
|
|||||||
|
|
||||||
return <GestureDetector gesture={nativeGesture}>{children}</GestureDetector>
|
return <GestureDetector gesture={nativeGesture}>{children}</GestureDetector>
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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"
|
resolved "https://registry.yarnpkg.com/react-native-mmkv/-/react-native-mmkv-2.12.2.tgz#4bba0f5f04e2cf222494cce3a9794ba6a4894dee"
|
||||||
integrity sha512-6058Aq0p57chPrUutLGe9fYoiDVDNMU2PKV+lLFUJ3GhoHvUrLdsS1PDSCLr00yqzL4WJQ7TTzH+V8cpyrNcfg==
|
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:
|
react-native-progress@bluesky-social/react-native-progress:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4"
|
resolved "https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4"
|
||||||
|
|||||||
Reference in New Issue
Block a user