Hide/show header and footer without re-renders, take two (#1849)

* Remove callsites using the state value

* Remove unused code

* Change shell mode without re-renders

* Adjust "write your reply" for mode
zio/stable
dan 2023-11-09 00:25:27 +00:00 committed by GitHub
parent bd531f2344
commit 82059b7ee1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 63 additions and 55 deletions

View File

@ -1,60 +1,61 @@
import React from 'react'
import {autorun} from 'mobx'
import {
Easing,
AnimatableValue,
interpolate,
useAnimatedStyle,
useSharedValue,
withTiming,
Easing,
} from 'react-native-reanimated'
import {useMinimalShellMode as useMinimalShellModeState} from '#/state/shell/minimal-mode'
function withShellTiming<T extends AnimatableValue>(value: T): T {
'worklet'
return withTiming(value, {
duration: 125,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
})
}
export function useMinimalShellMode() {
const minimalShellMode = useMinimalShellModeState()
const minimalShellInterp = useSharedValue(0)
const mode = useMinimalShellModeState()
const footerMinimalShellTransform = useAnimatedStyle(() => {
return {
opacity: interpolate(minimalShellInterp.value, [0, 1], [1, 0]),
pointerEvents: mode.value ? 'none' : 'auto',
opacity: withShellTiming(interpolate(mode.value ? 1 : 0, [0, 1], [1, 0])),
transform: [
{translateY: interpolate(minimalShellInterp.value, [0, 1], [0, 25])},
{
translateY: withShellTiming(
interpolate(mode.value ? 1 : 0, [0, 1], [0, 25]),
),
},
],
}
})
const headerMinimalShellTransform = useAnimatedStyle(() => {
return {
opacity: interpolate(minimalShellInterp.value, [0, 1], [1, 0]),
pointerEvents: mode.value ? 'none' : 'auto',
opacity: withShellTiming(interpolate(mode.value ? 1 : 0, [0, 1], [1, 0])),
transform: [
{translateY: interpolate(minimalShellInterp.value, [0, 1], [0, -25])},
{
translateY: withShellTiming(
interpolate(mode.value ? 1 : 0, [0, 1], [0, -25]),
),
},
],
}
})
const fabMinimalShellTransform = useAnimatedStyle(() => {
return {
transform: [
{translateY: interpolate(minimalShellInterp.value, [0, 1], [-44, 0])},
{
translateY: withShellTiming(
interpolate(mode.value ? 1 : 0, [0, 1], [-44, 0]),
),
},
],
}
})
React.useEffect(() => {
return autorun(() => {
if (minimalShellMode) {
minimalShellInterp.value = withTiming(1, {
duration: 125,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
})
} else {
minimalShellInterp.value = withTiming(0, {
duration: 125,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
})
}
})
}, [minimalShellInterp, minimalShellMode])
return {
minimalShellMode,
footerMinimalShellTransform,
headerMinimalShellTransform,
fabMinimalShellTransform,

View File

@ -33,9 +33,12 @@ export function useOnMainScroll(): [OnScrollCb, boolean, ResetCb] {
const dy = y - (lastY.current || 0)
lastY.current = y
if (!minimalShellMode && dy > dyLimitDown && y > Y_LIMIT) {
if (!minimalShellMode.value && dy > dyLimitDown && y > Y_LIMIT) {
setMinimalShellMode(true)
} else if (minimalShellMode && (dy < dyLimitUp * -1 || y <= Y_LIMIT)) {
} else if (
minimalShellMode.value &&
(dy < dyLimitUp * -1 || y <= Y_LIMIT)
) {
setMinimalShellMode(false)
}

View File

@ -1,16 +1,28 @@
import React from 'react'
import {useSharedValue, SharedValue} from 'react-native-reanimated'
type StateContext = boolean
type StateContext = SharedValue<boolean>
type SetContext = (v: boolean) => void
const stateContext = React.createContext<StateContext>(false)
const stateContext = React.createContext<StateContext>({
value: false,
addListener() {},
removeListener() {},
modify() {},
})
const setContext = React.createContext<SetContext>((_: boolean) => {})
export function Provider({children}: React.PropsWithChildren<{}>) {
const [state, setState] = React.useState(false)
const mode = useSharedValue(false)
const setMode = React.useCallback(
(v: boolean) => {
mode.value = v
},
[mode],
)
return (
<stateContext.Provider value={state}>
<setContext.Provider value={setState}>{children}</setContext.Provider>
<stateContext.Provider value={mode}>
<setContext.Provider value={setMode}>{children}</setContext.Provider>
</stateContext.Provider>
)
}

View File

@ -25,7 +25,7 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl(
const setDrawerOpen = useSetDrawerOpen()
const items = useHomeTabs(store.preferences.pinnedFeeds)
const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3)
const {minimalShellMode, headerMinimalShellTransform} = useMinimalShellMode()
const {headerMinimalShellTransform} = useMinimalShellMode()
const onPressAvi = React.useCallback(() => {
setDrawerOpen(true)
@ -38,7 +38,6 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl(
pal.border,
styles.tabBar,
headerMinimalShellTransform,
minimalShellMode && styles.disabled,
]}>
<View style={[pal.view, styles.topBar]}>
<View style={[pal.view]}>
@ -110,7 +109,4 @@ const styles = StyleSheet.create({
title: {
fontSize: 21,
},
disabled: {
pointerEvents: 'none',
},
})

View File

@ -1,5 +1,6 @@
import React, {useMemo} from 'react'
import {InteractionManager, StyleSheet, View} from 'react-native'
import Animated from 'react-native-reanimated'
import {useFocusEffect} from '@react-navigation/native'
import {observer} from 'mobx-react-lite'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
@ -15,15 +16,14 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {clamp} from 'lodash'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {logger} from '#/logger'
import {useMinimalShellMode, useSetMinimalShellMode} from '#/state/shell'
const SHELL_FOOTER_HEIGHT = 44
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
import {useSetMinimalShellMode} from '#/state/shell'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
export const PostThreadScreen = withAuthRequired(
observer(function PostThreadScreenImpl({route}: Props) {
const store = useStores()
const minimalShellMode = useMinimalShellMode()
const {fabMinimalShellTransform} = useMinimalShellMode()
const setMinimalShellMode = useSetMinimalShellMode()
const safeAreaInsets = useSafeAreaInsets()
const {name, rkey} = route.params
@ -83,17 +83,17 @@ export const PostThreadScreen = withAuthRequired(
treeView={!!store.preferences.thread.lab_treeViewEnabled}
/>
</View>
{isMobile && !minimalShellMode && (
<View
{isMobile && (
<Animated.View
style={[
styles.prompt,
fabMinimalShellTransform,
{
bottom:
SHELL_FOOTER_HEIGHT + clamp(safeAreaInsets.bottom, 15, 30),
bottom: clamp(safeAreaInsets.bottom, 15, 30),
},
]}>
<ComposePrompt onPressCompose={onPressReply} />
</View>
</Animated.View>
)}
</View>
)

View File

@ -39,7 +39,7 @@ export const BottomBar = observer(function BottomBarImpl({
const {isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile} =
useNavigationTabState()
const {minimalShellMode, footerMinimalShellTransform} = useMinimalShellMode()
const {footerMinimalShellTransform} = useMinimalShellMode()
const {notifications} = store.me
const onPressTab = React.useCallback(
@ -85,7 +85,6 @@ export const BottomBar = observer(function BottomBarImpl({
pal.border,
{paddingBottom: clamp(safeAreaInsets.bottom, 15, 30)},
footerMinimalShellTransform,
minimalShellMode && styles.disabled,
]}>
<Btn
testID="bottomBarHomeBtn"

View File

@ -65,7 +65,4 @@ export const styles = StyleSheet.create({
borderWidth: 1,
borderRadius: 100,
},
disabled: {
pointerEvents: 'none',
},
})