Simultaneous gesture

This commit is contained in:
Dan Abramov
2025-01-17 03:23:34 +00:00
parent 3b58dc8640
commit 9b8365e66b
+46 -27
View File
@@ -7,7 +7,11 @@ import {
View, View,
ViewToken, ViewToken,
} from 'react-native' } from 'react-native'
import {Gesture, GestureDetector} from 'react-native-gesture-handler' import {
Gesture,
GestureDetector,
NativeGesture,
} from 'react-native-gesture-handler'
import Animated, { import Animated, {
runOnJS, runOnJS,
runOnUI, runOnUI,
@@ -157,6 +161,8 @@ function Inner() {
const [currentIndex, setCurrentIndex] = useState(0) const [currentIndex, setCurrentIndex] = useState(0)
const scrollGesture = useMemo(() => Gesture.Native(), [])
const renderItem: ListRenderItem<FeedPostSliceItem> = useCallback( const renderItem: ListRenderItem<FeedPostSliceItem> = useCallback(
({item, index}) => { ({item, index}) => {
const {post} = item const {post} = item
@@ -177,10 +183,11 @@ function Inner() {
index === currentIndex && index === currentIndex &&
currentSource === post.embed.playlist currentSource === post.embed.playlist
} }
scrollGesture={scrollGesture}
/> />
) )
}, },
[players, currentIndex, isFocused, currentSources], [players, currentIndex, isFocused, currentSources, scrollGesture],
) )
const updateVideoState = useNonReactiveCallback((index?: number) => { const updateVideoState = useNonReactiveCallback((index?: number) => {
@@ -307,29 +314,31 @@ function Inner() {
) )
return ( return (
<List <GestureDetector gesture={scrollGesture}>
data={videos} <List
renderItem={renderItem} data={videos}
keyExtractor={keyExtractor} renderItem={renderItem}
pagingEnabled={true} keyExtractor={keyExtractor}
refreshing={isFetching} pagingEnabled={true}
onRefresh={refetch} refreshing={isFetching}
ListFooterComponent={ onRefresh={refetch}
<ListFooter ListFooterComponent={
hasNextPage={hasNextPage} <ListFooter
isFetchingNextPage={isFetchingNextPage} hasNextPage={hasNextPage}
onRetry={refetch} isFetchingNextPage={isFetchingNextPage}
/> onRetry={refetch}
} />
onEndReached={() => {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage()
} }
}} onEndReached={() => {
showsVerticalScrollIndicator={false} if (hasNextPage && !isFetchingNextPage) {
onViewableItemsChanged={onViewableItemsChanged} fetchNextPage()
viewabilityConfig={{itemVisiblePercentThreshold: 95}} }
/> }}
showsVerticalScrollIndicator={false}
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={{itemVisiblePercentThreshold: 95}}
/>
</GestureDetector>
) )
} }
@@ -342,11 +351,13 @@ function VideoItem({
post, post,
embed, embed,
active, active,
scrollGesture,
}: { }: {
player?: VideoPlayer player?: VideoPlayer
post: AppBskyFeedDefs.PostView post: AppBskyFeedDefs.PostView
embed: AppBskyEmbedVideo.View embed: AppBskyEmbedVideo.View
active: boolean active: boolean
scrollGesture: NativeGesture
}) { }) {
const postShadow = usePostShadow(post) const postShadow = usePostShadow(post)
const {height, width} = useWindowDimensions() const {height, width} = useWindowDimensions()
@@ -382,7 +393,12 @@ function VideoItem({
)} )}
{postShadow !== POST_TOMBSTONE ? ( {postShadow !== POST_TOMBSTONE ? (
player && ( player && (
<Overlay player={player} post={postShadow} active={active} /> <Overlay
player={player}
post={postShadow}
active={active}
scrollGesture={scrollGesture}
/>
) )
) : ( ) : (
<View <View
@@ -466,10 +482,12 @@ function Overlay({
player, player,
post, post,
active, active,
scrollGesture,
}: { }: {
player: VideoPlayer player: VideoPlayer
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<AppBskyFeedDefs.PostView>
active: boolean active: boolean
scrollGesture: NativeGesture
}) { }) {
const insets = useSafeAreaInsets() const insets = useSafeAreaInsets()
const t = useTheme() const t = useTheme()
@@ -504,19 +522,20 @@ function Overlay({
const gesture = useMemo(() => { const gesture = useMemo(() => {
const dragLeftGesture = Gesture.Pan() const dragLeftGesture = Gesture.Pan()
.simultaneousWithExternalGesture(scrollGesture)
.activeOffsetX([0, 10]) .activeOffsetX([0, 10])
.failOffsetX([-10, 0]) .failOffsetX([-10, 0])
.failOffsetY([-5, 5]) .failOffsetY([-5, 5])
.maxPointers(1) .maxPointers(1)
.onEnd(evt => { .onEnd(evt => {
'worklet' 'worklet'
if (evt.translationX < -50) { if (evt.translationX < -50 && evt.velocityX < -300) {
runOnJS(pushToProfile)() runOnJS(pushToProfile)()
} }
}) })
return dragLeftGesture return dragLeftGesture
}, [pushToProfile]) }, [pushToProfile, scrollGesture])
const rkey = new AtUri(post.uri).rkey const rkey = new AtUri(post.uri).rkey
const record = AppBskyFeedPost.isRecord(post.record) ? post.record : undefined const record = AppBskyFeedPost.isRecord(post.record) ? post.record : undefined