From 82059b7ee138d24ff50b0f4fad0eaeac860bb78c Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 9 Nov 2023 00:25:27 +0000 Subject: [PATCH] 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 --- src/lib/hooks/useMinimalShellMode.tsx | 59 ++++++++++--------- src/lib/hooks/useOnMainScroll.ts | 7 ++- src/state/shell/minimal-mode.tsx | 22 +++++-- src/view/com/pager/FeedsTabBarMobile.tsx | 6 +- src/view/screens/PostThread.tsx | 18 +++--- src/view/shell/bottom-bar/BottomBar.tsx | 3 +- src/view/shell/bottom-bar/BottomBarStyles.tsx | 3 - 7 files changed, 63 insertions(+), 55 deletions(-) diff --git a/src/lib/hooks/useMinimalShellMode.tsx b/src/lib/hooks/useMinimalShellMode.tsx index ada934a2..4738b8e2 100644 --- a/src/lib/hooks/useMinimalShellMode.tsx +++ b/src/lib/hooks/useMinimalShellMode.tsx @@ -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(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, diff --git a/src/lib/hooks/useOnMainScroll.ts b/src/lib/hooks/useOnMainScroll.ts index 2eab4b25..a213d531 100644 --- a/src/lib/hooks/useOnMainScroll.ts +++ b/src/lib/hooks/useOnMainScroll.ts @@ -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) } diff --git a/src/state/shell/minimal-mode.tsx b/src/state/shell/minimal-mode.tsx index 4909a9a6..b506c21d 100644 --- a/src/state/shell/minimal-mode.tsx +++ b/src/state/shell/minimal-mode.tsx @@ -1,16 +1,28 @@ import React from 'react' +import {useSharedValue, SharedValue} from 'react-native-reanimated' -type StateContext = boolean +type StateContext = SharedValue type SetContext = (v: boolean) => void -const stateContext = React.createContext(false) +const stateContext = React.createContext({ + value: false, + addListener() {}, + removeListener() {}, + modify() {}, +}) const setContext = React.createContext((_: 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 ( - - {children} + + {children} ) } diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/pager/FeedsTabBarMobile.tsx index 9848ce2d..b33829ee 100644 --- a/src/view/com/pager/FeedsTabBarMobile.tsx +++ b/src/view/com/pager/FeedsTabBarMobile.tsx @@ -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, ]}> @@ -110,7 +109,4 @@ const styles = StyleSheet.create({ title: { fontSize: 21, }, - disabled: { - pointerEvents: 'none', - }, }) diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx index 0bdd0626..0abce45f 100644 --- a/src/view/screens/PostThread.tsx +++ b/src/view/screens/PostThread.tsx @@ -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 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} /> - {isMobile && !minimalShellMode && ( - - + )} ) diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index fedfcdfc..db4fa9d7 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -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, ]}>