diff options
author | dan <dan.abramov@gmail.com> | 2024-08-22 23:27:33 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-22 23:27:33 +0100 |
commit | b8dbb71781997c9b8d595e7760f99b30a5e199e5 (patch) | |
tree | 308a70dc5c9e2441319009cd8144c4c2d00113b9 /src/view/com/util/MainScrollProvider.tsx | |
parent | 2ae3ffcf782e10bddcf1fdbbc3983724f605e711 (diff) | |
download | voidsky-b8dbb71781997c9b8d595e7760f99b30a5e199e5.tar.zst |
Fix fixed footer experiment (#4969)
* Split minimal shell mode into headerMode and footerMode For now, we'll always write them in sync. When we read them, we'll use headerMode as source of truth. This will let us keep footerMode independent in a future commit. * Remove fixed_bottom_bar special cases during calculation This isn't the right time to determine special behavior. Instead we'll adjust footerMode itself conditionally on the gate. * Copy-paste setMode into MainScrollProvider This lets us fork the implementation later just for this case. * Gate footer adjustment in MainScrollProvider This is the final piece. Normal calls to setMode() keep setting both header and footer, but MainScrollProvider adjusts the footer conditionally.
Diffstat (limited to 'src/view/com/util/MainScrollProvider.tsx')
-rw-r--r-- | src/view/com/util/MainScrollProvider.tsx | 53 |
1 files changed, 41 insertions, 12 deletions
diff --git a/src/view/com/util/MainScrollProvider.tsx b/src/view/com/util/MainScrollProvider.tsx index b602da432..3163d8544 100644 --- a/src/view/com/util/MainScrollProvider.tsx +++ b/src/view/com/util/MainScrollProvider.tsx @@ -4,11 +4,13 @@ import { cancelAnimation, interpolate, useSharedValue, + withSpring, } from 'react-native-reanimated' import EventEmitter from 'eventemitter3' import {ScrollProvider} from '#/lib/ScrollContext' -import {useMinimalShellMode, useSetMinimalShellMode} from '#/state/shell' +import {useGate} from '#/lib/statsig/statsig' +import {useMinimalShellMode} from '#/state/shell' import {useShellLayout} from '#/state/shell/shell-layout' import {isNative, isWeb} from 'platform/detection' @@ -21,11 +23,29 @@ function clamp(num: number, min: number, max: number) { export function MainScrollProvider({children}: {children: React.ReactNode}) { const {headerHeight} = useShellLayout() - const mode = useMinimalShellMode() - const setMode = useSetMinimalShellMode() + const {headerMode, footerMode} = useMinimalShellMode() const startDragOffset = useSharedValue<number | null>(null) const startMode = useSharedValue<number | null>(null) const didJustRestoreScroll = useSharedValue<boolean>(false) + const gate = useGate() + const isFixedBottomBar = gate('fixed_bottom_bar') + + const setMode = React.useCallback( + (v: boolean) => { + 'worklet' + cancelAnimation(headerMode) + headerMode.value = withSpring(v ? 1 : 0, { + overshootClamping: true, + }) + if (!isFixedBottomBar) { + cancelAnimation(footerMode) + footerMode.value = withSpring(v ? 1 : 0, { + overshootClamping: true, + }) + } + }, + [headerMode, footerMode, isFixedBottomBar], + ) useEffect(() => { if (isWeb) { @@ -55,11 +75,11 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { setMode(true) } else { // Snap to whichever state is the closest. - setMode(Math.round(mode.value) === 1) + setMode(Math.round(headerMode.value) === 1) } } }, - [startDragOffset, startMode, setMode, mode, headerHeight], + [startDragOffset, startMode, setMode, headerMode, headerHeight], ) const onBeginDrag = useCallback( @@ -67,10 +87,10 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { 'worklet' if (isNative) { startDragOffset.value = e.contentOffset.y - startMode.value = mode.value + startMode.value = headerMode.value } }, - [mode, startDragOffset, startMode], + [headerMode, startDragOffset, startMode], ) const onEndDrag = useCallback( @@ -102,7 +122,10 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { 'worklet' if (isNative) { if (startDragOffset.value === null || startMode.value === null) { - if (mode.value !== 0 && e.contentOffset.y < headerHeight.value) { + if ( + headerMode.value !== 0 && + e.contentOffset.y < headerHeight.value + ) { // If we're close enough to the top, always show the shell. // Even if we're not dragging. setMode(false) @@ -119,11 +142,15 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { [-1, 1], ) const newValue = clamp(startMode.value + dProgress, 0, 1) - if (newValue !== mode.value) { + if (newValue !== headerMode.value) { // Manually adjust the value. This won't be (and shouldn't be) animated. // Cancel any any existing animation - cancelAnimation(mode) - mode.value = newValue + cancelAnimation(headerMode) + headerMode.value = newValue + if (!isFixedBottomBar) { + cancelAnimation(footerMode) + footerMode.value = newValue + } } } else { if (didJustRestoreScroll.value) { @@ -145,11 +172,13 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { }, [ headerHeight, - mode, + headerMode, + footerMode, setMode, startDragOffset, startMode, didJustRestoreScroll, + isFixedBottomBar, ], ) |