Refactor feed header components (#2964)
* Move home-related files to view/com/home * Add HomeHeader in front of FeedTabBar * Move isDekstop check outside FeedsTabBar * Remove PWI logic from tabbar * Separate platform-specific layout from shared logiczio/stable
parent
93b5eff4d7
commit
1ccb3be961
|
@ -0,0 +1,71 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
|
import {HomeHeaderLayout} from './HomeHeaderLayout'
|
||||||
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
|
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||||
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
|
import {isWeb} from 'platform/detection'
|
||||||
|
import {TabBar} from '../pager/TabBar'
|
||||||
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
|
|
||||||
|
export function HomeHeader(
|
||||||
|
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 {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
|
||||||
|
const pal = usePalette('default')
|
||||||
|
|
||||||
|
const items = React.useMemo(() => {
|
||||||
|
const pinnedNames = feeds.map(f => f.displayName)
|
||||||
|
|
||||||
|
if (!hasPinnedCustom) {
|
||||||
|
return pinnedNames.concat('Feeds ✨')
|
||||||
|
}
|
||||||
|
return pinnedNames
|
||||||
|
}, [hasPinnedCustom, feeds])
|
||||||
|
|
||||||
|
const onPressFeedsLink = React.useCallback(() => {
|
||||||
|
if (isWeb) {
|
||||||
|
navigation.navigate('Feeds')
|
||||||
|
} else {
|
||||||
|
navigation.navigate('FeedsTab')
|
||||||
|
navigation.popToTop()
|
||||||
|
}
|
||||||
|
}, [navigation])
|
||||||
|
|
||||||
|
const onSelect = React.useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
if (!hasPinnedCustom && index === items.length - 1) {
|
||||||
|
onPressFeedsLink()
|
||||||
|
} else if (props.onSelect) {
|
||||||
|
props.onSelect(index)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[items.length, onPressFeedsLink, props, hasPinnedCustom],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HomeHeaderLayout>
|
||||||
|
<TabBar
|
||||||
|
key={items.join(',')}
|
||||||
|
onPressSelected={props.onPressSelected}
|
||||||
|
selectedPage={props.selectedPage}
|
||||||
|
onSelect={onSelect}
|
||||||
|
testID={props.testID}
|
||||||
|
items={items}
|
||||||
|
indicatorColor={pal.colors.link}
|
||||||
|
/>
|
||||||
|
</HomeHeaderLayout>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export {HomeHeaderLayoutMobile as HomeHeaderLayout} from './HomeHeaderLayoutMobile'
|
|
@ -0,0 +1,50 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {StyleSheet} from 'react-native'
|
||||||
|
import Animated from 'react-native-reanimated'
|
||||||
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
|
import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile'
|
||||||
|
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
|
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||||
|
|
||||||
|
export function HomeHeaderLayout({children}: {children: React.ReactNode}) {
|
||||||
|
const {isMobile} = useWebMediaQueries()
|
||||||
|
if (isMobile) {
|
||||||
|
return <HomeHeaderLayoutMobile>{children}</HomeHeaderLayoutMobile>
|
||||||
|
} else {
|
||||||
|
return <HomeHeaderLayoutTablet>{children}</HomeHeaderLayoutTablet>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function HomeHeaderLayoutTablet({children}: {children: React.ReactNode}) {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
const {headerMinimalShellTransform} = useMinimalShellMode()
|
||||||
|
const {headerHeight} = useShellLayout()
|
||||||
|
|
||||||
|
return (
|
||||||
|
// @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
|
||||||
|
<Animated.View
|
||||||
|
style={[pal.view, pal.border, styles.tabBar, headerMinimalShellTransform]}
|
||||||
|
onLayout={e => {
|
||||||
|
headerHeight.value = e.nativeEvent.layout.height
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</Animated.View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
tabBar: {
|
||||||
|
// @ts-ignore Web only
|
||||||
|
position: 'sticky',
|
||||||
|
zIndex: 1,
|
||||||
|
// @ts-ignore Web only -prf
|
||||||
|
left: 'calc(50% - 300px)',
|
||||||
|
width: 600,
|
||||||
|
top: 0,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderLeftWidth: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,7 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {TabBar} from 'view/com/pager/TabBar'
|
|
||||||
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
|
@ -13,11 +11,7 @@ import {useLingui} from '@lingui/react'
|
||||||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
import {useSetDrawerOpen} from '#/state/shell/drawer-open'
|
import {useSetDrawerOpen} from '#/state/shell/drawer-open'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
|
||||||
import {isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
|
||||||
import {Logo} from '#/view/icons/Logo'
|
import {Logo} from '#/view/icons/Logo'
|
||||||
|
|
||||||
import {IS_DEV} from '#/env'
|
import {IS_DEV} from '#/env'
|
||||||
|
@ -25,49 +19,17 @@ import {atoms} from '#/alf'
|
||||||
import {Link as Link2} from '#/components/Link'
|
import {Link as Link2} from '#/components/Link'
|
||||||
import {ColorPalette_Stroke2_Corner0_Rounded as ColorPalette} from '#/components/icons/ColorPalette'
|
import {ColorPalette_Stroke2_Corner0_Rounded as ColorPalette} from '#/components/icons/ColorPalette'
|
||||||
|
|
||||||
export function FeedsTabBar(
|
export function HomeHeaderLayoutMobile({
|
||||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
children,
|
||||||
) {
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {hasSession} = useSession()
|
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const setDrawerOpen = useSetDrawerOpen()
|
const setDrawerOpen = useSetDrawerOpen()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
|
||||||
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
|
|
||||||
const {headerHeight} = useShellLayout()
|
const {headerHeight} = useShellLayout()
|
||||||
const {headerMinimalShellTransform} = useMinimalShellMode()
|
const {headerMinimalShellTransform} = useMinimalShellMode()
|
||||||
|
|
||||||
const items = React.useMemo(() => {
|
|
||||||
if (!hasSession) return []
|
|
||||||
|
|
||||||
const pinnedNames = feeds.map(f => f.displayName)
|
|
||||||
|
|
||||||
if (!hasPinnedCustom) {
|
|
||||||
return pinnedNames.concat('Feeds ✨')
|
|
||||||
}
|
|
||||||
return pinnedNames
|
|
||||||
}, [hasSession, hasPinnedCustom, feeds])
|
|
||||||
|
|
||||||
const onPressFeedsLink = React.useCallback(() => {
|
|
||||||
if (isWeb) {
|
|
||||||
navigation.navigate('Feeds')
|
|
||||||
} else {
|
|
||||||
navigation.navigate('FeedsTab')
|
|
||||||
navigation.popToTop()
|
|
||||||
}
|
|
||||||
}, [navigation])
|
|
||||||
|
|
||||||
const onSelect = React.useCallback(
|
|
||||||
(index: number) => {
|
|
||||||
if (hasSession && !hasPinnedCustom && index === items.length - 1) {
|
|
||||||
onPressFeedsLink()
|
|
||||||
} else if (props.onSelect) {
|
|
||||||
props.onSelect(index)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[items.length, onPressFeedsLink, props, hasSession, hasPinnedCustom],
|
|
||||||
)
|
|
||||||
|
|
||||||
const onPressAvi = React.useCallback(() => {
|
const onPressAvi = React.useCallback(() => {
|
||||||
setDrawerOpen(true)
|
setDrawerOpen(true)
|
||||||
}, [setDrawerOpen])
|
}, [setDrawerOpen])
|
||||||
|
@ -113,35 +75,21 @@ export function FeedsTabBar(
|
||||||
<ColorPalette size="md" />
|
<ColorPalette size="md" />
|
||||||
</Link2>
|
</Link2>
|
||||||
)}
|
)}
|
||||||
|
<Link
|
||||||
{hasSession && (
|
testID="viewHeaderHomeFeedPrefsBtn"
|
||||||
<Link
|
href="/settings/home-feed"
|
||||||
testID="viewHeaderHomeFeedPrefsBtn"
|
hitSlop={HITSLOP_10}
|
||||||
href="/settings/home-feed"
|
accessibilityRole="button"
|
||||||
hitSlop={HITSLOP_10}
|
accessibilityLabel={_(msg`Home Feed Preferences`)}
|
||||||
accessibilityRole="button"
|
accessibilityHint="">
|
||||||
accessibilityLabel={_(msg`Home Feed Preferences`)}
|
<FontAwesomeIcon
|
||||||
accessibilityHint="">
|
icon="sliders"
|
||||||
<FontAwesomeIcon
|
style={pal.textLight as FontAwesomeIconStyle}
|
||||||
icon="sliders"
|
/>
|
||||||
style={pal.textLight as FontAwesomeIconStyle}
|
</Link>
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
{children}
|
||||||
{items.length > 0 && (
|
|
||||||
<TabBar
|
|
||||||
key={items.join(',')}
|
|
||||||
onPressSelected={props.onPressSelected}
|
|
||||||
selectedPage={props.selectedPage}
|
|
||||||
onSelect={onSelect}
|
|
||||||
testID={props.testID}
|
|
||||||
items={items}
|
|
||||||
indicatorColor={pal.colors.link}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1 +0,0 @@
|
||||||
export * from './FeedsTabBarMobile'
|
|
|
@ -1,138 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import {View, StyleSheet} from 'react-native'
|
|
||||||
import Animated from 'react-native-reanimated'
|
|
||||||
import {TabBar} from 'view/com/pager/TabBar'
|
|
||||||
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|
||||||
import {FeedsTabBar as FeedsTabBarMobile} from './FeedsTabBarMobile'
|
|
||||||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
|
||||||
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {TextLink} from '#/view/com/util/Link'
|
|
||||||
import {CenteredView} from '../util/Views'
|
|
||||||
import {isWeb} from 'platform/detection'
|
|
||||||
import {useNavigation} from '@react-navigation/native'
|
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
|
||||||
|
|
||||||
export function FeedsTabBar(
|
|
||||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
|
||||||
) {
|
|
||||||
const {isMobile, isTablet} = useWebMediaQueries()
|
|
||||||
const {hasSession} = useSession()
|
|
||||||
|
|
||||||
if (isMobile) {
|
|
||||||
return <FeedsTabBarMobile {...props} />
|
|
||||||
} else if (isTablet) {
|
|
||||||
if (hasSession) {
|
|
||||||
return <FeedsTabBarTablet {...props} />
|
|
||||||
} else {
|
|
||||||
return <FeedsTabBarPublic />
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function FeedsTabBarPublic() {
|
|
||||||
const pal = usePalette('default')
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CenteredView sideBorders>
|
|
||||||
<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 "
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</CenteredView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FeedsTabBarTablet(
|
|
||||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
|
||||||
) {
|
|
||||||
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const {hasSession} = useSession()
|
|
||||||
const navigation = useNavigation<NavigationProp>()
|
|
||||||
const {headerMinimalShellTransform} = useMinimalShellMode()
|
|
||||||
const {headerHeight} = useShellLayout()
|
|
||||||
|
|
||||||
const items = React.useMemo(() => {
|
|
||||||
if (!hasSession) return []
|
|
||||||
|
|
||||||
const pinnedNames = feeds.map(f => f.displayName)
|
|
||||||
|
|
||||||
if (!hasPinnedCustom) {
|
|
||||||
return pinnedNames.concat('Feeds ✨')
|
|
||||||
}
|
|
||||||
return pinnedNames
|
|
||||||
}, [hasSession, hasPinnedCustom, feeds])
|
|
||||||
|
|
||||||
const onPressDiscoverFeeds = React.useCallback(() => {
|
|
||||||
if (isWeb) {
|
|
||||||
navigation.navigate('Feeds')
|
|
||||||
} else {
|
|
||||||
navigation.navigate('FeedsTab')
|
|
||||||
navigation.popToTop()
|
|
||||||
}
|
|
||||||
}, [navigation])
|
|
||||||
|
|
||||||
const onSelect = React.useCallback(
|
|
||||||
(index: number) => {
|
|
||||||
if (hasSession && !hasPinnedCustom && index === items.length - 1) {
|
|
||||||
onPressDiscoverFeeds()
|
|
||||||
} else if (props.onSelect) {
|
|
||||||
props.onSelect(index)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[items.length, onPressDiscoverFeeds, props, hasSession, hasPinnedCustom],
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
// @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
|
|
||||||
<Animated.View
|
|
||||||
style={[pal.view, pal.border, styles.tabBar, headerMinimalShellTransform]}
|
|
||||||
onLayout={e => {
|
|
||||||
headerHeight.value = e.nativeEvent.layout.height
|
|
||||||
}}>
|
|
||||||
<TabBar
|
|
||||||
key={items.join(',')}
|
|
||||||
{...props}
|
|
||||||
onSelect={onSelect}
|
|
||||||
items={items}
|
|
||||||
indicatorColor={pal.colors.link}
|
|
||||||
/>
|
|
||||||
</Animated.View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
tabBar: {
|
|
||||||
// @ts-ignore Web only
|
|
||||||
position: 'sticky',
|
|
||||||
zIndex: 1,
|
|
||||||
// @ts-ignore Web only -prf
|
|
||||||
left: 'calc(50% - 300px)',
|
|
||||||
width: 600,
|
|
||||||
top: 0,
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
borderLeftWidth: 1,
|
|
||||||
borderRightWidth: 1,
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -6,7 +6,7 @@ import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
||||||
import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
|
import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
|
||||||
import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed'
|
import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed'
|
||||||
import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState'
|
import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState'
|
||||||
import {FeedsTabBar} from '../com/pager/FeedsTabBar'
|
import {HomeHeader} from '../com/home/HomeHeader'
|
||||||
import {Pager, RenderTabBarFnProps, PagerRef} from 'view/com/pager/Pager'
|
import {Pager, RenderTabBarFnProps, PagerRef} from 'view/com/pager/Pager'
|
||||||
import {FeedPage} from 'view/com/feeds/FeedPage'
|
import {FeedPage} from 'view/com/feeds/FeedPage'
|
||||||
import {HomeLoggedOutCTA} from '../com/auth/HomeLoggedOutCTA'
|
import {HomeLoggedOutCTA} from '../com/auth/HomeLoggedOutCTA'
|
||||||
|
@ -118,7 +118,7 @@ function HomeScreenReady({
|
||||||
const renderTabBar = React.useCallback(
|
const renderTabBar = React.useCallback(
|
||||||
(props: RenderTabBarFnProps) => {
|
(props: RenderTabBarFnProps) => {
|
||||||
return (
|
return (
|
||||||
<FeedsTabBar
|
<HomeHeader
|
||||||
key="FEEDS_TAB_BAR"
|
key="FEEDS_TAB_BAR"
|
||||||
selectedPage={props.selectedPage}
|
selectedPage={props.selectedPage}
|
||||||
onSelect={props.onSelect}
|
onSelect={props.onSelect}
|
||||||
|
|
Loading…
Reference in New Issue