Improve navigation gesture and fix caching issue

zio/stable
Paul Frazee 2022-09-07 14:43:00 -05:00
parent 69265753bf
commit 5ae39612d7
2 changed files with 58 additions and 48 deletions

View File

@ -1,6 +1,7 @@
import React, {useState, useRef, useMemo} from 'react' import React, {useState, useRef, useMemo} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import { import {
useWindowDimensions,
GestureResponderEvent, GestureResponderEvent,
Image, Image,
SafeAreaView, SafeAreaView,
@ -16,6 +17,7 @@ import Animated, {
useAnimatedStyle, useAnimatedStyle,
withTiming, withTiming,
runOnJS, runOnJS,
interpolate,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {IconProp} from '@fortawesome/fontawesome-svg-core' import {IconProp} from '@fortawesome/fontawesome-svg-core'
@ -33,7 +35,7 @@ import {AVIS} from '../../lib/assets'
const locationIconNeedsNudgeUp = (icon: IconProp) => icon === 'house' const locationIconNeedsNudgeUp = (icon: IconProp) => icon === 'house'
const SWIPE_GESTURE_HIT_SLOP = {left: 0, top: 0, width: 20, bottom: 0} const SWIPE_GESTURE_HIT_SLOP = {left: 0, top: 0, width: 20, bottom: 0}
const SWIPE_GESTURE_MAX_DISTANCE = 150 const SWIPE_GESTURE_TRIGGER = 0.5
const Location = ({ const Location = ({
icon, icon,
@ -106,7 +108,8 @@ export const MobileShell: React.FC = observer(() => {
const store = useStores() const store = useStores()
const tabSelectorRef = useRef<{open: () => void}>() const tabSelectorRef = useRef<{open: () => void}>()
const [isLocationMenuActive, setLocationMenuActive] = useState(false) const [isLocationMenuActive, setLocationMenuActive] = useState(false)
const swipeGesturePosition = useSharedValue<number>(0) const winDim = useWindowDimensions()
const swipeGestureInterp = useSharedValue<number>(0)
const screenRenderDesc = constructScreenRenderDesc(store.nav) const screenRenderDesc = constructScreenRenderDesc(store.nav)
const onPressAvi = () => const onPressAvi = () =>
@ -145,24 +148,31 @@ export const MobileShell: React.FC = observer(() => {
Gesture.Pan() Gesture.Pan()
.hitSlop(SWIPE_GESTURE_HIT_SLOP) .hitSlop(SWIPE_GESTURE_HIT_SLOP)
.onUpdate(e => { .onUpdate(e => {
swipeGesturePosition.value = Math.min( if (store.nav.tab.canGoBack) {
e.translationX, swipeGestureInterp.value = Math.max(
SWIPE_GESTURE_MAX_DISTANCE, e.translationX / winDim.width,
0,
) )
}
}) })
.onEnd(e => { .onEnd(_e => {
if (swipeGesturePosition.value >= SWIPE_GESTURE_MAX_DISTANCE) { if (swipeGestureInterp.value >= SWIPE_GESTURE_TRIGGER) {
runOnJS(goBack)() runOnJS(goBack)()
swipeGesturePosition.value = 0 swipeGestureInterp.value = withTiming(1, {duration: 100}, () => {
swipeGestureInterp.value = 0
})
} else { } else {
swipeGesturePosition.value = withTiming(0, {duration: 100}) swipeGestureInterp.value = withTiming(0, {duration: 100})
} }
}), }),
[swipeGesturePosition, goBack], [swipeGestureInterp, winDim, store.nav.tab.canGoBack, goBack],
) )
const swipeViewAnimatedStyle = useAnimatedStyle(() => ({ const swipeTransform = useAnimatedStyle(() => ({
transform: [{translateX: swipeGesturePosition.value}], transform: [{translateX: swipeGestureInterp.value * winDim.width}],
}))
const swipeOpacity = useAnimatedStyle(() => ({
opacity: interpolate(swipeGestureInterp.value, [0, 1.0], [0.6, 0.0]),
})) }))
return ( return (
@ -181,39 +191,34 @@ export const MobileShell: React.FC = observer(() => {
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<SafeAreaView style={styles.innerContainer}> <SafeAreaView style={styles.innerContainer}>
<GestureDetector gesture={swipeGesture}>
<ScreenContainer style={styles.screenContainer}> <ScreenContainer style={styles.screenContainer}>
{screenRenderDesc.screens.map(({Com, params, key, current}) => { {screenRenderDesc.screens.map(
if (current && store.nav.tab.canGoBack) { ({Com, params, key, current, previous}) => {
return ( return (
<Screen <Screen
key={key} key={key}
style={[StyleSheet.absoluteFill]} style={[StyleSheet.absoluteFill]}
activityState={2}> activityState={current ? 2 : previous ? 1 : 0}>
<GestureDetector gesture={swipeGesture}>
<Animated.View <Animated.View
style={[s.flex1, styles.screen, swipeViewAnimatedStyle]}> style={
<FontAwesomeIcon current ? [styles.screenMask, swipeOpacity] : undefined
icon="arrow-left" }
size={30}
style={[styles.swipeGestureIcon]}
/> />
<Animated.View
style={[
s.flex1,
styles.screen,
current ? swipeTransform : undefined,
]}>
<Com params={params} visible={true} /> <Com params={params} visible={true} />
</Animated.View> </Animated.View>
</GestureDetector>
</Screen> </Screen>
) )
} else { },
return ( )}
<Screen
key={key}
style={[StyleSheet.absoluteFill, styles.screen]}
activityState={current ? 2 : 0}>
<Com params={params} visible={current} />
</Screen>
)
}
})}
</ScreenContainer> </ScreenContainer>
</GestureDetector>
</SafeAreaView> </SafeAreaView>
<View style={styles.bottomBar}> <View style={styles.bottomBar}>
<Btn <Btn
@ -274,6 +279,7 @@ function constructScreenRenderDesc(nav: NavigationModel): {
] ]
const parsedTabScreens = tabScreens.map(screen => { const parsedTabScreens = tabScreens.map(screen => {
const isCurrent = nav.isCurrentScreen(tab.id, screen.index) const isCurrent = nav.isCurrentScreen(tab.id, screen.index)
const isPrevious = nav.isCurrentScreen(tab.id, screen.index + 1)
const matchRes = match(screen.url) const matchRes = match(screen.url)
if (isCurrent) { if (isCurrent) {
icon = matchRes.icon icon = matchRes.icon
@ -281,6 +287,7 @@ function constructScreenRenderDesc(nav: NavigationModel): {
return Object.assign(matchRes, { return Object.assign(matchRes, {
key: `t${tab.id}-s${screen.index}`, key: `t${tab.id}-s${screen.index}`,
current: isCurrent, current: isCurrent,
previous: isPrevious,
}) as ScreenRenderDesc }) as ScreenRenderDesc
}) })
screens = screens.concat(parsedTabScreens) screens = screens.concat(parsedTabScreens)
@ -304,10 +311,14 @@ const styles = StyleSheet.create({
screen: { screen: {
backgroundColor: colors.gray1, backgroundColor: colors.gray1,
}, },
swipeGestureIcon: { screenMask: {
position: 'absolute', position: 'absolute',
left: -75, top: 0,
top: '50%', bottom: 0,
left: 0,
right: 0,
backgroundColor: '#000',
opacity: 0.5,
}, },
topBar: { topBar: {
flexDirection: 'row', flexDirection: 'row',

View File

@ -22,4 +22,3 @@ Paul's todo list
- Follows list - Follows list
- Bugs - Bugs
- Check that sub components arent reloading too much - Check that sub components arent reloading too much
- Profile isn't caching on nav anymore