Simple feed-end component with trends on mobile
(cherry picked from commit ccfea4b9461111701586f39e8e3e1bfad187708c)
This commit is contained in:
@@ -0,0 +1,129 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
import {msg, Trans} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {useNavigationDeduped} from '#/lib/hooks/useNavigationDeduped'
|
||||||
|
import {isNative} from '#/platform/detection'
|
||||||
|
import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics'
|
||||||
|
import {useTrendingConfig} from '#/state/trending-config'
|
||||||
|
import {atoms as a, useBreakpoints,useGutters, useTheme, web} from '#/alf'
|
||||||
|
import {Button, ButtonIcon,ButtonText} from '#/components/Button'
|
||||||
|
import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2'
|
||||||
|
import {Trending2_Stroke2_Corner2_Rounded as Graph} from '#/components/icons/Trending2'
|
||||||
|
import {
|
||||||
|
TrendingTopic,
|
||||||
|
TrendingTopicLink,
|
||||||
|
TrendingTopicSkeleton,
|
||||||
|
} from '#/components/TrendingTopics'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
|
const TRENDS_COUNT = 9
|
||||||
|
|
||||||
|
export function FeedEnd() {
|
||||||
|
const t = useTheme()
|
||||||
|
const {_} = useLingui()
|
||||||
|
const gutters = useGutters(['wide'])
|
||||||
|
const {enabled} = useTrendingConfig()
|
||||||
|
const navigation = useNavigationDeduped()
|
||||||
|
const {gtTablet} = useBreakpoints()
|
||||||
|
|
||||||
|
const onPressExplore = React.useCallback(() => {
|
||||||
|
if (isNative) {
|
||||||
|
navigation.navigate('SearchTab')
|
||||||
|
navigation.popToTop()
|
||||||
|
} else {
|
||||||
|
navigation.navigate('Search', {})
|
||||||
|
}
|
||||||
|
}, [navigation])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.align_center,
|
||||||
|
gutters,
|
||||||
|
a.py_5xl,
|
||||||
|
a.border_t,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
]}>
|
||||||
|
<View style={[a.gap_sm, a.pb_2xl]}>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.text_sm,
|
||||||
|
a.leading_snug,
|
||||||
|
a.text_center,
|
||||||
|
{
|
||||||
|
maxWidth: 200,
|
||||||
|
},
|
||||||
|
web({
|
||||||
|
maxWidth: '12rem',
|
||||||
|
}),
|
||||||
|
]}>
|
||||||
|
<Trans>That's it! You've reached the end of this feed.</Trans>
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
label={_(msg`Explore Bluesky`)}
|
||||||
|
onPress={onPressExplore}
|
||||||
|
size="small"
|
||||||
|
variant="solid"
|
||||||
|
color="secondary"
|
||||||
|
style={[a.justify_center]}>
|
||||||
|
<ButtonIcon icon={Search} position="left" />
|
||||||
|
<ButtonText>
|
||||||
|
<Trans>Explore Bluesky</Trans>
|
||||||
|
</ButtonText>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{enabled && !gtTablet ? <Trending /> : null}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Trending() {
|
||||||
|
const t = useTheme()
|
||||||
|
const {data: trending, error, isLoading} = useTrendingTopics()
|
||||||
|
const noTopics = !isLoading && !error && !trending?.topics?.length
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
|
|
||||||
|
return error || noTopics ? null : (
|
||||||
|
<View style={[a.gap_sm, gtMobile && a.px_2xl]}>
|
||||||
|
<View style={[a.flex_row, a.align_center, a.justify_center, a.gap_xs]}>
|
||||||
|
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||||
|
<Trans>Check out what's trending</Trans>
|
||||||
|
</Text>
|
||||||
|
<Graph size="sm" />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.flex_row, a.flex_wrap, a.justify_center, a.gap_sm]}>
|
||||||
|
{isLoading ? (
|
||||||
|
Array(TRENDS_COUNT)
|
||||||
|
.fill(0)
|
||||||
|
.map((_n, i) => (
|
||||||
|
<TrendingTopicSkeleton key={i} size="small" index={i} />
|
||||||
|
))
|
||||||
|
) : !trending?.topics ? null : (
|
||||||
|
<>
|
||||||
|
{trending.topics.slice(0, TRENDS_COUNT).map(topic => (
|
||||||
|
<TrendingTopicLink key={topic.link} topic={topic}>
|
||||||
|
{({hovered}) => (
|
||||||
|
<TrendingTopic
|
||||||
|
size="small"
|
||||||
|
topic={topic}
|
||||||
|
style={[
|
||||||
|
hovered && [
|
||||||
|
t.atoms.border_contrast_high,
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</TrendingTopicLink>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -25,10 +25,8 @@ import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
|
|||||||
import {FeedPage} from '#/view/com/feeds/FeedPage'
|
import {FeedPage} from '#/view/com/feeds/FeedPage'
|
||||||
import {HomeHeader} from '#/view/com/home/HomeHeader'
|
import {HomeHeader} from '#/view/com/home/HomeHeader'
|
||||||
import {Pager, PagerRef, RenderTabBarFnProps} from '#/view/com/pager/Pager'
|
import {Pager, PagerRef, RenderTabBarFnProps} from '#/view/com/pager/Pager'
|
||||||
import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState'
|
|
||||||
import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState'
|
|
||||||
import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed'
|
|
||||||
import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
|
import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
|
||||||
|
import {FeedEnd} from '#/components/FeedEnd'
|
||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home' | 'Start'>
|
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home' | 'Start'>
|
||||||
@@ -192,14 +190,6 @@ function HomeScreenReady({
|
|||||||
[onPressSelected, pinnedFeedInfos],
|
[onPressSelected, pinnedFeedInfos],
|
||||||
)
|
)
|
||||||
|
|
||||||
const renderFollowingEmptyState = React.useCallback(() => {
|
|
||||||
return <FollowingEmptyState />
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const renderCustomFeedEmptyState = React.useCallback(() => {
|
|
||||||
return <CustomFeedEmptyState />
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const homeFeedParams = React.useMemo<FeedParams>(() => {
|
const homeFeedParams = React.useMemo<FeedParams>(() => {
|
||||||
return {
|
return {
|
||||||
mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
||||||
@@ -232,8 +222,8 @@ function HomeScreenReady({
|
|||||||
isPageAdjacent={Math.abs(selectedIndex - index) === 1}
|
isPageAdjacent={Math.abs(selectedIndex - index) === 1}
|
||||||
feed={feed}
|
feed={feed}
|
||||||
feedParams={homeFeedParams}
|
feedParams={homeFeedParams}
|
||||||
renderEmptyState={renderFollowingEmptyState}
|
renderEmptyState={FeedEnd}
|
||||||
renderEndOfFeed={FollowingEndOfFeed}
|
renderEndOfFeed={FeedEnd}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -245,8 +235,9 @@ function HomeScreenReady({
|
|||||||
isPageFocused={selectedFeed === feed}
|
isPageFocused={selectedFeed === feed}
|
||||||
isPageAdjacent={Math.abs(selectedIndex - index) === 1}
|
isPageAdjacent={Math.abs(selectedIndex - index) === 1}
|
||||||
feed={feed}
|
feed={feed}
|
||||||
renderEmptyState={renderCustomFeedEmptyState}
|
renderEmptyState={FeedEnd}
|
||||||
savedFeedConfig={savedFeedConfig}
|
savedFeedConfig={savedFeedConfig}
|
||||||
|
renderEndOfFeed={FeedEnd}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -265,7 +256,7 @@ function HomeScreenReady({
|
|||||||
isPageFocused
|
isPageFocused
|
||||||
isPageAdjacent={false}
|
isPageAdjacent={false}
|
||||||
feed={`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`}
|
feed={`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`}
|
||||||
renderEmptyState={renderCustomFeedEmptyState}
|
renderEmptyState={FeedEnd}
|
||||||
/>
|
/>
|
||||||
</Pager>
|
</Pager>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user