From e3643fcbef4d7ef10cda9e56ad541691f52c13f6 Mon Sep 17 00:00:00 2001 From: Dylan Audius Date: Mon, 24 Aug 2026 18:04:00 -0700 Subject: [PATCH] feat(mobile): hide the play bar with the rest of the bottom chrome MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The now-playing bar did not participate in the auto-hide, so scrolling down with a track playing left it stranded: the tab bar floats away beneath it and the play bar stayed put with an empty band underneath. It travels on the same `useTabBarHiddenProgress` the tab bar uses, so the two move as one unit rather than on two signals that can drift apart. This translates the whole drawer, not the play bar inside it. The white surface behind the play bar belongs to the drawer root, so moving only its contents slides the bar off a background that stays put — trading the empty band for an empty white block. Wrapping the drawer means restating its z-order, since the wrapper creates a new stacking context, and `box-none` so the full-screen wrapper doesn't swallow touches meant for the screen behind it. Pinned to 0 while the drawer is open: the same drawer is the full-screen player, and translating it there would drag the expanded player off screen. Verified on the iOS simulator with a track playing — chrome hides and restores as one unit with no empty band; the full-screen player still opens, still swipe-dismisses, and the left nav drawer still pushes the bar with the screen rather than being punctured by its elevation. Co-Authored-By: Claude Opus 5 --- .../now-playing-drawer/NowPlayingDrawer.tsx | 192 +++++++++++------- 1 file changed, 121 insertions(+), 71 deletions(-) diff --git a/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx b/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx index 25472f28643..4f8c1b04275 100644 --- a/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx +++ b/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx @@ -17,7 +17,15 @@ import type { GestureResponderEvent, PanResponderGestureState } from 'react-native' -import { Keyboard, Platform, View, StatusBar, Pressable } from 'react-native' +import { + Keyboard, + Platform, + View, + StatusBar, + Pressable, + StyleSheet +} from 'react-native' +import Reanimated, { useAnimatedStyle } from 'react-native-reanimated' import { useSafeAreaInsets } from 'react-native-safe-area-context' import TrackPlayer from 'react-native-track-player' import { useDispatch, useSelector } from 'react-redux' @@ -33,6 +41,7 @@ import { useDrawer } from 'app/hooks/useDrawer' import { useNavigation } from 'app/hooks/useNavigation' import { AppDrawerContext } from 'app/screens/app-drawer-screen' import { AppTabNavigationContext } from 'app/screens/app-screen' +import { useTabBarHiddenProgress } from 'app/screens/app-screen/TabBarAutoHideContext' import { makeStyles } from 'app/styles' import { zIndex } from 'app/utils/zIndex' @@ -110,6 +119,7 @@ export const NowPlayingDrawer = memo(function NowPlayingDrawer( const styles = useStyles() const { isOpen, onOpen, onClose } = useDrawer('NowPlaying') + const tabBarHidden = useTabBarHiddenProgress() // Defer to the comments bottom-sheet while it's presented: its swipe-to-dismiss // gesture would otherwise leak through and also close this drawer underneath. const { isOpen: isCommentDrawerOpen } = useCommentDrawer() @@ -170,6 +180,31 @@ export const NowPlayingDrawer = memo(function NowPlayingDrawer( isOpen && isDrawerFullyOpen ? staticTopInset.current : insets.top const effectiveBottomInset = isOpen && isDrawerFullyOpen ? 0 : insets.bottom + // Travel with the bottom tab bar so the bottom chrome moves as one unit. + // The collapsed drawer rests `BOTTOM_BAR_HEIGHT + PLAY_BAR_HEIGHT` off the + // bottom, so once the tab bar floats away the play bar would otherwise hang + // there with an empty band beneath it. + // + // This moves the whole drawer rather than just the play bar inside it: the + // white surface behind the play bar belongs to the drawer root, so + // translating only its contents slides the bar off a background that stays + // put, leaving an empty white block. Pinned to 0 while open, since the same + // drawer is the full-screen player and translating that would drag the + // expanded player off screen with it. + const chromeHideStyle = useAnimatedStyle( + () => ({ + transform: [ + { + translateY: isOpen + ? 0 + : (PLAY_BAR_HEIGHT + BOTTOM_BAR_HEIGHT + insets.bottom) * + tabBarHidden.value + } + ] + }), + [isOpen, insets.bottom] + ) + useEffect(() => { if ( Platform.OS === 'ios' && @@ -281,79 +316,94 @@ export const NowPlayingDrawer = memo(function NowPlayingDrawer( }, [onClose, navigation, trackId]) return ( - - - - - - - - - - - - - - - - - - - - - + ]} + > + + + + + + + + + + + + + + + + + + + + - - + + ) })