diff options
Diffstat (limited to 'src/view/com/util/ViewSelector.tsx')
-rw-r--r-- | src/view/com/util/ViewSelector.tsx | 107 |
1 files changed, 31 insertions, 76 deletions
diff --git a/src/view/com/util/ViewSelector.tsx b/src/view/com/util/ViewSelector.tsx index b404988f3..f7652334f 100644 --- a/src/view/com/util/ViewSelector.tsx +++ b/src/view/com/util/ViewSelector.tsx @@ -1,15 +1,13 @@ import React, {useEffect, useState, useMemo} from 'react' import {FlatList, StyleSheet, View} from 'react-native' -import {GestureDetector, Gesture} from 'react-native-gesture-handler' -import {useSharedValue, withTiming, runOnJS} from 'react-native-reanimated' import {Selector} from './Selector' +import {HorzSwipe} from './gestures/HorzSwipe' +import {useAnimatedValue} from '../../lib/useAnimatedValue' +import {useStores} from '../../../state' const HEADER_ITEM = {_reactKey: '__header__'} const SELECTOR_ITEM = {_reactKey: '__selector__'} const STICKY_HEADER_INDICES = [1] -const SWIPE_GESTURE_MAX_DISTANCE = 200 -const SWIPE_GESTURE_VEL_TRIGGER = 2000 -const SWIPE_GESTURE_HIT_SLOP = {left: -50, top: 0, right: 0, bottom: 0} // we ignore the left 20 pixels to avoid conflicts with the page-nav gesture export function ViewSelector({ sections, @@ -32,72 +30,26 @@ export function ViewSelector({ onRefresh?: () => void onEndReached?: (info: {distanceFromEnd: number}) => void }) { + const store = useStores() const [selectedIndex, setSelectedIndex] = useState<number>(0) - const swipeGestureInterp = useSharedValue<number>(0) + const panX = useAnimatedValue(0) // events // = + const onSwipeEnd = (dx: number) => { + if (dx !== 0) { + setSelectedIndex(selectedIndex + dx) + } + } const onPressSelection = (index: number) => setSelectedIndex(index) useEffect(() => { + store.shell.setViewControllingSwipes( + Boolean(swipeEnabled) && selectedIndex > 0, + ) onSelectView?.(selectedIndex) }, [selectedIndex]) - // gestures - // = - - const swipeGesture = useMemo(() => { - if (!swipeEnabled) return undefined - return Gesture.Pan() - .hitSlop(SWIPE_GESTURE_HIT_SLOP) - .onUpdate(e => { - // calculate [-1, 1] range for the gesture - const clamped = Math.min(e.translationX, SWIPE_GESTURE_MAX_DISTANCE) - const reversed = clamped * -1 - const scaled = reversed / SWIPE_GESTURE_MAX_DISTANCE - swipeGestureInterp.value = scaled - }) - .onEnd(e => { - const vx = e.velocityX - if ( - swipeGestureInterp.value >= 0.5 || - (vx < 0 && Math.abs(vx) > SWIPE_GESTURE_VEL_TRIGGER) - ) { - // swiped to next - if (selectedIndex < sections.length - 1) { - // interp to the next item's position... - swipeGestureInterp.value = withTiming(1, {duration: 100}, () => { - // ...then update the index, which triggers the useEffect() below [1] - runOnJS(setSelectedIndex)(selectedIndex + 1) - }) - } else { - swipeGestureInterp.value = withTiming(0, {duration: 100}) - } - } else if ( - swipeGestureInterp.value <= -0.5 || - (vx > 0 && Math.abs(vx) > SWIPE_GESTURE_VEL_TRIGGER) - ) { - // swiped to prev - if (selectedIndex > 0) { - // interp to the prev item's position... - swipeGestureInterp.value = withTiming(-1, {duration: 100}, () => { - // ...then update the index, which triggers the useEffect() below [1] - runOnJS(setSelectedIndex)(selectedIndex - 1) - }) - } else { - swipeGestureInterp.value = withTiming(0, {duration: 100}) - } - } else { - swipeGestureInterp.value = withTiming(0, {duration: 100}) - } - }) - }, [swipeEnabled, swipeGestureInterp, selectedIndex, sections.length]) - useEffect(() => { - // [1] completes the swipe gesture animation by resetting the interp value - // this has to be done as an effect so that it occurs *after* the selectedIndex has been updated - swipeGestureInterp.value = 0 - }, [swipeGestureInterp, selectedIndex]) - // rendering // = @@ -111,8 +63,8 @@ export function ViewSelector({ return ( <Selector items={sections} + panX={panX} selectedIndex={selectedIndex} - swipeGestureInterp={swipeGestureInterp} onSelect={onPressSelection} /> ) @@ -122,21 +74,24 @@ export function ViewSelector({ } const data = [HEADER_ITEM, SELECTOR_ITEM, ...items] - const listEl = ( - <FlatList - data={data} - keyExtractor={item => item._reactKey} - renderItem={renderItemInternal} - stickyHeaderIndices={STICKY_HEADER_INDICES} - refreshing={refreshing} - onRefresh={onRefresh} - onEndReached={onEndReached} - /> + return ( + <HorzSwipe + panX={panX} + swipeEnabled={swipeEnabled || false} + canSwipeLeft={selectedIndex > 0} + canSwipeRight={selectedIndex < sections.length - 1} + onSwipeEnd={onSwipeEnd}> + <FlatList + data={data} + keyExtractor={item => item._reactKey} + renderItem={renderItemInternal} + stickyHeaderIndices={STICKY_HEADER_INDICES} + refreshing={refreshing} + onRefresh={onRefresh} + onEndReached={onEndReached} + /> + </HorzSwipe> ) - if (swipeEnabled) { - return <GestureDetector gesture={swipeGesture}>{listEl}</GestureDetector> - } - return listEl } const styles = StyleSheet.create({}) |