Sync the bottom bar to screen transitions and scroll

## Summary

- Hook the bottom bar up to react-native-screens' transition progress. Every `Layout.Screen` now exposes a `presence` shared value (via `useScreenPresence`) that follows the native push/pop/swipe frame by frame, so screens with `minimalShell` hide and reveal the bar in sync with the transition instead of after it. Fixes the delayed tab bar when leaving a conversation.
- Replace the minimal-shell and hide-border refcounts with a small registry that sums 0..1 contributions on the UI thread. The bottom bar border now fades with the screen rather than toggling.
- Bring back the scroll-linked bottom bar on Home (removed in #5203). The header and footer share the same drag-linked value, and the bar is revealed on feed change, blur, and app foreground so badges aren't missed. Web included.

## Test plan

- [ ] Swipe back from a conversation: bar tracks the swipe, including a cancelled swipe
- [ ] Back button from a conversation: bar animates with the pop
- [ ] Push a profile over a conversation and pop back
- [ ] Post thread: bottom bar border fades in/out with the screen
- [ ] Home: scroll down hides header and bar together, scroll up shows them
- [ ] Home: scrolled down, swipe into a convo and back - no jump
- [ ] Home: bar reappears on feed switch, on leaving Home, and on foregrounding the app
- [ ] Deep link / notification into a conversation from another tab
- [ ] Mobile web: bar hides/shows on scroll and on conversation screen
- [ ] Rapid swipe-back and swipe-back while scrolling (software-mansion/react-native-reanimated#9402, software-mansion/react-native-screens#2659)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JWQciPDiipBhqiW922Nivs
This commit is contained in:
Claude
2026-09-07 19:40:57 +00:00
parent 08069e2877
commit 2dc7071e7d
12 changed files with 525 additions and 152 deletions
+3 -5
View File
@@ -11,7 +11,7 @@ import {PressableScale} from '#/lib/custom-animations/PressableScale'
import {BOTTOM_BAR_AVI} from '#/lib/demo'
import {useHaptics} from '#/lib/haptics'
import {useDedupe} from '#/lib/hooks/useDedupe'
import {useHideBottomBarBorder} from '#/lib/hooks/useHideBottomBarBorder'
import {useBottomBarBorderStyle} from '#/lib/hooks/useHideBottomBarBorder'
import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransform'
import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
import {clamp} from '#/lib/numbers'
@@ -80,7 +80,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
const accountSwitchControl = useDialogControl()
const messagesMenuControl = Menu.useMenuControl()
const playHaptic = useHaptics()
const hideBorder = useHideBottomBarBorder()
const borderStyle = useBottomBarBorderStyle()
const iconWidth = 28
const showSignIn = useCallback(() => {
@@ -165,9 +165,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
style={[
styles.bottomBar,
t.atoms.bg,
hideBorder
? {borderColor: t.atoms.bg.backgroundColor}
: t.atoms.border_contrast_low,
borderStyle,
{paddingBottom: clamp(safeAreaInsets.bottom, 15, 60)},
footerMinimalShellTransform,
]}
+3 -5
View File
@@ -6,7 +6,7 @@ import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {useNavigationState} from '@react-navigation/native'
import {useHideBottomBarBorder} from '#/lib/hooks/useHideBottomBarBorder'
import {useBottomBarBorderStyle} from '#/lib/hooks/useHideBottomBarBorder'
import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransform'
import {getCurrentRoute, isTab} from '#/lib/routes/helpers'
import {makeProfileLink} from '#/lib/routes/links'
@@ -57,7 +57,7 @@ export function BottomBarWeb() {
const {requestSwitchToAccount} = useLoggedOutViewControls()
const closeAllActiveElements = useCloseAllActiveElements()
const {footerHeight} = useShellLayout()
const hideBorder = useHideBottomBarBorder()
const borderStyle = useBottomBarBorderStyle()
const accountSwitchControl = useDialogControl()
const {data: profile} = useProfileQuery({did: currentAccount?.did})
const iconWidth = 26
@@ -92,9 +92,7 @@ export function BottomBarWeb() {
styles.bottomBar,
styles.bottomBarWeb,
t.atoms.bg,
hideBorder
? {borderColor: t.atoms.bg.backgroundColor}
: t.atoms.border_contrast_low,
borderStyle,
footerMinimalShellTransform,
]}
onLayout={event => footerHeight.set(event.nativeEvent.layout.height)}>