Show tab bar on desktop web (#2998)
* Show tabbar on desktop * Make bottom border always 1px * Don't hide/show navbar when switching tabs * two rows WIP * Top bar tweaks * Make scroll adjustement native-only * Add new web scroll behaviorzio/stable
parent
978bcc1ba9
commit
ac726497a4
|
@ -1,30 +1,24 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
|
||||||
FontAwesomeIcon,
|
|
||||||
FontAwesomeIconStyle,
|
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||||
import {MainScrollProvider} from '../util/MainScrollProvider'
|
import {MainScrollProvider} from '../util/MainScrollProvider'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {useSetMinimalShellMode} from '#/state/shell'
|
import {useSetMinimalShellMode} from '#/state/shell'
|
||||||
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
||||||
import {ComposeIcon2} from 'lib/icons'
|
import {ComposeIcon2} from 'lib/icons'
|
||||||
import {colors, s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
import {View, useWindowDimensions} from 'react-native'
|
import {View, useWindowDimensions} from 'react-native'
|
||||||
import {ListMethods} from '../util/List'
|
import {ListMethods} from '../util/List'
|
||||||
import {Feed} from '../posts/Feed'
|
import {Feed} from '../posts/Feed'
|
||||||
import {TextLink} from '../util/Link'
|
|
||||||
import {FAB} from '../util/fab/FAB'
|
import {FAB} from '../util/fab/FAB'
|
||||||
import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn'
|
import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
import {listenSoftReset, emitSoftReset} from '#/state/events'
|
import {listenSoftReset} from '#/state/events'
|
||||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||||
import {TabState, getTabState, getRootNavigation} from '#/lib/routes/helpers'
|
import {TabState, getTabState, getRootNavigation} from '#/lib/routes/helpers'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
|
@ -47,10 +41,8 @@ export function FeedPage({
|
||||||
renderEndOfFeed?: () => JSX.Element
|
renderEndOfFeed?: () => JSX.Element
|
||||||
}) {
|
}) {
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const pal = usePalette('default')
|
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const {isDesktop} = useWebMediaQueries()
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const {openComposer} = useComposerControls()
|
const {openComposer} = useComposerControls()
|
||||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||||
|
@ -99,63 +91,6 @@ export function FeedPage({
|
||||||
setHasNew(false)
|
setHasNew(false)
|
||||||
}, [scrollToTop, feed, queryClient, setHasNew])
|
}, [scrollToTop, feed, queryClient, setHasNew])
|
||||||
|
|
||||||
const ListHeaderComponent = React.useCallback(() => {
|
|
||||||
if (isDesktop) {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pal.view,
|
|
||||||
{
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
paddingHorizontal: 18,
|
|
||||||
paddingVertical: 12,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<TextLink
|
|
||||||
type="title-lg"
|
|
||||||
href="/"
|
|
||||||
style={[pal.text, {fontWeight: 'bold'}]}
|
|
||||||
text={
|
|
||||||
<>
|
|
||||||
Bluesky{' '}
|
|
||||||
{hasNew && (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
top: -8,
|
|
||||||
backgroundColor: colors.blue3,
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
borderRadius: 4,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
onPress={emitSoftReset}
|
|
||||||
/>
|
|
||||||
{hasSession && (
|
|
||||||
<TextLink
|
|
||||||
type="title-lg"
|
|
||||||
href="/settings/following-feed"
|
|
||||||
style={{fontWeight: 'bold'}}
|
|
||||||
accessibilityLabel={_(msg`Feed Preferences`)}
|
|
||||||
accessibilityHint=""
|
|
||||||
text={
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon="sliders"
|
|
||||||
style={pal.textLight as FontAwesomeIconStyle}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return <></>
|
|
||||||
}, [isDesktop, pal.view, pal.text, pal.textLight, hasNew, _, hasSession])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View testID={testID} style={s.h100pct}>
|
<View testID={testID} style={s.h100pct}>
|
||||||
<MainScrollProvider>
|
<MainScrollProvider>
|
||||||
|
@ -171,7 +106,6 @@ export function FeedPage({
|
||||||
onHasNew={setHasNew}
|
onHasNew={setHasNew}
|
||||||
renderEmptyState={renderEmptyState}
|
renderEmptyState={renderEmptyState}
|
||||||
renderEndOfFeed={renderEndOfFeed}
|
renderEndOfFeed={renderEndOfFeed}
|
||||||
ListHeaderComponent={ListHeaderComponent}
|
|
||||||
headerOffset={headerOffset}
|
headerOffset={headerOffset}
|
||||||
/>
|
/>
|
||||||
</MainScrollProvider>
|
</MainScrollProvider>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
import {HomeHeaderLayout} from './HomeHeaderLayout'
|
import {HomeHeaderLayout} from './HomeHeaderLayout'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|
||||||
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
|
@ -11,16 +10,6 @@ import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
|
|
||||||
export function HomeHeader(
|
export function HomeHeader(
|
||||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
||||||
) {
|
|
||||||
const {isDesktop} = useWebMediaQueries()
|
|
||||||
if (isDesktop) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return <HomeHeaderInner {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HomeHeaderInner(
|
|
||||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
|
||||||
) {
|
) {
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
|
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import Animated from 'react-native-reanimated'
|
import Animated from 'react-native-reanimated'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile'
|
import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile'
|
||||||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||||
|
import {Logo} from '#/view/icons/Logo'
|
||||||
|
import {Link, TextLink} from '../util/Link'
|
||||||
|
import {
|
||||||
|
FontAwesomeIcon,
|
||||||
|
FontAwesomeIconStyle,
|
||||||
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {msg} from '@lingui/macro'
|
||||||
|
import {CogIcon} from '#/lib/icons'
|
||||||
|
|
||||||
export function HomeHeaderLayout({children}: {children: React.ReactNode}) {
|
export function HomeHeaderLayout({children}: {children: React.ReactNode}) {
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
|
@ -20,6 +29,7 @@ function HomeHeaderLayoutTablet({children}: {children: React.ReactNode}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {headerMinimalShellTransform} = useMinimalShellMode()
|
const {headerMinimalShellTransform} = useMinimalShellMode()
|
||||||
const {headerHeight} = useShellLayout()
|
const {headerHeight} = useShellLayout()
|
||||||
|
const {_} = useLingui()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
|
// @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
|
||||||
|
@ -28,12 +38,44 @@ function HomeHeaderLayoutTablet({children}: {children: React.ReactNode}) {
|
||||||
onLayout={e => {
|
onLayout={e => {
|
||||||
headerHeight.value = e.nativeEvent.layout.height
|
headerHeight.value = e.nativeEvent.layout.height
|
||||||
}}>
|
}}>
|
||||||
|
<View style={[pal.view, styles.topBar]}>
|
||||||
|
<TextLink
|
||||||
|
type="title-lg"
|
||||||
|
href="/settings/following-feed"
|
||||||
|
accessibilityLabel={_(msg`Following Feed Preferences`)}
|
||||||
|
accessibilityHint=""
|
||||||
|
text={
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="sliders"
|
||||||
|
style={pal.textLight as FontAwesomeIconStyle}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Logo width={28} />
|
||||||
|
<Link
|
||||||
|
href="/settings/saved-feeds"
|
||||||
|
hitSlop={10}
|
||||||
|
accessibilityRole="button"
|
||||||
|
accessibilityLabel={_(msg`Edit Saved Feeds`)}
|
||||||
|
accessibilityHint={_(msg`Opens screen to edit Saved Feeds`)}>
|
||||||
|
<CogIcon size={22} strokeWidth={2} style={pal.textLight} />
|
||||||
|
</Link>
|
||||||
|
</View>
|
||||||
{children}
|
{children}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
topBar: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 18,
|
||||||
|
paddingVertical: 8,
|
||||||
|
marginTop: 8,
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
tabBar: {
|
tabBar: {
|
||||||
// @ts-ignore Web only
|
// @ts-ignore Web only
|
||||||
position: 'sticky',
|
position: 'sticky',
|
||||||
|
@ -42,7 +84,7 @@ const styles = StyleSheet.create({
|
||||||
left: 'calc(50% - 300px)',
|
left: 'calc(50% - 300px)',
|
||||||
width: 600,
|
width: 600,
|
||||||
top: 0,
|
top: 0,
|
||||||
flexDirection: 'row',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
borderLeftWidth: 1,
|
borderLeftWidth: 1,
|
||||||
borderRightWidth: 1,
|
borderRightWidth: 1,
|
||||||
|
|
|
@ -103,7 +103,6 @@ const styles = StyleSheet.create({
|
||||||
right: 0,
|
right: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
borderBottomWidth: 1,
|
|
||||||
},
|
},
|
||||||
topBar: {
|
topBar: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {PressableWithHover} from '../util/PressableWithHover'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {DraggableScrollView} from './DraggableScrollView'
|
import {DraggableScrollView} from './DraggableScrollView'
|
||||||
|
import {isNative} from '#/platform/detection'
|
||||||
|
|
||||||
export interface TabBarProps {
|
export interface TabBarProps {
|
||||||
testID?: string
|
testID?: string
|
||||||
|
@ -15,6 +16,10 @@ export interface TabBarProps {
|
||||||
onPressSelected?: (index: number) => void
|
onPressSelected?: (index: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// How much of the previous/next item we're showing
|
||||||
|
// to give the user a hint there's more to scroll.
|
||||||
|
const OFFSCREEN_ITEM_WIDTH = 20
|
||||||
|
|
||||||
export function TabBar({
|
export function TabBar({
|
||||||
testID,
|
testID,
|
||||||
selectedPage,
|
selectedPage,
|
||||||
|
@ -25,6 +30,7 @@ export function TabBar({
|
||||||
}: TabBarProps) {
|
}: TabBarProps) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const scrollElRef = useRef<ScrollView>(null)
|
const scrollElRef = useRef<ScrollView>(null)
|
||||||
|
const itemRefs = useRef<Array<Element>>([])
|
||||||
const [itemXs, setItemXs] = useState<number[]>([])
|
const [itemXs, setItemXs] = useState<number[]>([])
|
||||||
const indicatorStyle = useMemo(
|
const indicatorStyle = useMemo(
|
||||||
() => ({borderBottomColor: indicatorColor || pal.colors.link}),
|
() => ({borderBottomColor: indicatorColor || pal.colors.link}),
|
||||||
|
@ -33,12 +39,58 @@ export function TabBar({
|
||||||
const {isDesktop, isTablet} = useWebMediaQueries()
|
const {isDesktop, isTablet} = useWebMediaQueries()
|
||||||
const styles = isDesktop || isTablet ? desktopStyles : mobileStyles
|
const styles = isDesktop || isTablet ? desktopStyles : mobileStyles
|
||||||
|
|
||||||
// scrolls to the selected item when the page changes
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
scrollElRef.current?.scrollTo({
|
if (isNative) {
|
||||||
x:
|
// On native, the primary interaction is swiping.
|
||||||
(itemXs[selectedPage] || 0) - styles.contentContainer.paddingHorizontal,
|
// We adjust the scroll little by little on every tab change.
|
||||||
|
// Scroll into view but keep the end of the previous item visible.
|
||||||
|
let x = itemXs[selectedPage] || 0
|
||||||
|
x = Math.max(0, x - OFFSCREEN_ITEM_WIDTH)
|
||||||
|
scrollElRef.current?.scrollTo({x})
|
||||||
|
} else {
|
||||||
|
// On the web, the primary interaction is tapping.
|
||||||
|
// Scrolling under tap feels disorienting so only adjust the scroll offset
|
||||||
|
// when tapping on an item out of view--and we adjust by almost an entire page.
|
||||||
|
const parent = scrollElRef?.current?.getScrollableNode?.()
|
||||||
|
if (!parent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const parentRect = parent.getBoundingClientRect()
|
||||||
|
if (!parentRect) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
left: parentLeft,
|
||||||
|
right: parentRight,
|
||||||
|
width: parentWidth,
|
||||||
|
} = parentRect
|
||||||
|
const child = itemRefs.current[selectedPage]
|
||||||
|
if (!child) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const childRect = child.getBoundingClientRect?.()
|
||||||
|
if (!childRect) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const {left: childLeft, right: childRight, width: childWidth} = childRect
|
||||||
|
let dx = 0
|
||||||
|
if (childRight >= parentRight) {
|
||||||
|
dx += childRight - parentRight
|
||||||
|
dx += parentWidth - childWidth - OFFSCREEN_ITEM_WIDTH
|
||||||
|
} else if (childLeft <= parentLeft) {
|
||||||
|
dx -= parentLeft - childLeft
|
||||||
|
dx -= parentWidth - childWidth - OFFSCREEN_ITEM_WIDTH
|
||||||
|
}
|
||||||
|
let x = parent.scrollLeft + dx
|
||||||
|
x = Math.max(0, x)
|
||||||
|
x = Math.min(x, parent.scrollWidth - parentWidth)
|
||||||
|
if (dx !== 0) {
|
||||||
|
parent.scroll({
|
||||||
|
left: x,
|
||||||
|
behavior: 'smooth',
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}, [scrollElRef, itemXs, selectedPage, styles])
|
}, [scrollElRef, itemXs, selectedPage, styles])
|
||||||
|
|
||||||
const onPressItem = useCallback(
|
const onPressItem = useCallback(
|
||||||
|
@ -78,6 +130,7 @@ export function TabBar({
|
||||||
<PressableWithHover
|
<PressableWithHover
|
||||||
testID={`${testID}-selector-${i}`}
|
testID={`${testID}-selector-${i}`}
|
||||||
key={`${item}-${i}`}
|
key={`${item}-${i}`}
|
||||||
|
ref={node => (itemRefs.current[i] = node)}
|
||||||
onLayout={e => onItemLayout(e, i)}
|
onLayout={e => onItemLayout(e, i)}
|
||||||
style={styles.item}
|
style={styles.item}
|
||||||
hoverStyle={pal.viewLight}
|
hoverStyle={pal.viewLight}
|
||||||
|
@ -94,6 +147,7 @@ export function TabBar({
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</DraggableScrollView>
|
</DraggableScrollView>
|
||||||
|
<View style={[pal.border, styles.outerBottomBorder]} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -117,6 +171,13 @@ const desktopStyles = StyleSheet.create({
|
||||||
borderBottomWidth: 3,
|
borderBottomWidth: 3,
|
||||||
borderBottomColor: 'transparent',
|
borderBottomColor: 'transparent',
|
||||||
},
|
},
|
||||||
|
outerBottomBorder: {
|
||||||
|
position: 'absolute',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: -1,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const mobileStyles = StyleSheet.create({
|
const mobileStyles = StyleSheet.create({
|
||||||
|
@ -137,4 +198,11 @@ const mobileStyles = StyleSheet.create({
|
||||||
borderBottomWidth: 3,
|
borderBottomWidth: 3,
|
||||||
borderBottomColor: 'transparent',
|
borderBottomColor: 'transparent',
|
||||||
},
|
},
|
||||||
|
outerBottomBorder: {
|
||||||
|
position: 'absolute',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: -1,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -20,12 +20,14 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
|
||||||
const setMode = useSetMinimalShellMode()
|
const setMode = useSetMinimalShellMode()
|
||||||
const startDragOffset = useSharedValue<number | null>(null)
|
const startDragOffset = useSharedValue<number | null>(null)
|
||||||
const startMode = useSharedValue<number | null>(null)
|
const startMode = useSharedValue<number | null>(null)
|
||||||
|
const didJustRestoreScroll = useSharedValue<boolean>(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isWeb) {
|
if (isWeb) {
|
||||||
return listenToForcedWindowScroll(() => {
|
return listenToForcedWindowScroll(() => {
|
||||||
startDragOffset.value = null
|
startDragOffset.value = null
|
||||||
startMode.value = null
|
startMode.value = null
|
||||||
|
didJustRestoreScroll.value = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -86,6 +88,11 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
|
||||||
mode.value = newValue
|
mode.value = newValue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (didJustRestoreScroll.value) {
|
||||||
|
didJustRestoreScroll.value = false
|
||||||
|
// Don't hide/show navbar based on scroll restoratoin.
|
||||||
|
return
|
||||||
|
}
|
||||||
// On the web, we don't try to follow the drag because we don't know when it ends.
|
// On the web, we don't try to follow the drag because we don't know when it ends.
|
||||||
// Instead, show/hide immediately based on whether we're scrolling up or down.
|
// Instead, show/hide immediately based on whether we're scrolling up or down.
|
||||||
const dy = e.contentOffset.y - (startDragOffset.value ?? 0)
|
const dy = e.contentOffset.y - (startDragOffset.value ?? 0)
|
||||||
|
@ -98,7 +105,14 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[headerHeight, mode, setMode, startDragOffset, startMode],
|
[
|
||||||
|
headerHeight,
|
||||||
|
mode,
|
||||||
|
setMode,
|
||||||
|
startDragOffset,
|
||||||
|
startMode,
|
||||||
|
didJustRestoreScroll,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
Loading…
Reference in New Issue