Update presentation of trending feeds (#11206)

This commit is contained in:
DS Boyce
2026-07-22 14:35:45 -07:00
parent 64f3b6a3ae
commit 3cb7c1cd96
6 changed files with 252 additions and 170 deletions
-15
View File
@@ -1055,11 +1055,6 @@
"count": 1
}
},
"src/screens/Profile/components/ProfileFeedHeader.tsx": {
"typescript/no-misused-promises": {
"count": 5
}
},
"src/screens/ProfileList/FeedSection.tsx": {
"typescript/no-floating-promises": {
"count": 1
@@ -1406,11 +1401,6 @@
"count": 2
}
},
"src/state/queries/feed.ts": {
"typescript/no-floating-promises": {
"count": 1
}
},
"src/state/queries/handle.ts": {
"typescript/no-floating-promises": {
"count": 1
@@ -1756,11 +1746,6 @@
"count": 2
}
},
"src/view/com/posts/PostFeed.tsx": {
"typescript/no-explicit-any": {
"count": 1
}
},
"src/view/com/posts/PostFeedErrorMessage.tsx": {
"typescript/no-explicit-any": {
"count": 1
+3
View File
@@ -146,6 +146,9 @@ export const BSKY_FEED_OWNER_DIDS = [
'did:plc:q6gjnaw2blty4crticxkmujt',
]
export const TRENDING_DID = 'did:plc:qrz3lhbyuxbeilrc6nekdqme'
export const TRENDING_HANDLE = 'trending.bsky.app'
export const DISCOVER_FEED_URI =
'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot'
export const VIDEO_FEED_URI =
+15 -14
View File
@@ -1,13 +1,12 @@
import {useCallback, useEffect, useMemo, useState} from 'react'
import {useAnimatedRef} from 'react-native-reanimated'
import {AppBskyFeedDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useLingui} from '@lingui/react/macro'
import {useIsFocused} from '@react-navigation/native'
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
import {useQueryClient} from '@tanstack/react-query'
import {VIDEO_FEED_URIS} from '#/lib/constants'
import {TRENDING_DID, TRENDING_HANDLE, VIDEO_FEED_URIS} from '#/lib/constants'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {useSetTitle} from '#/lib/hooks/useSetTitle'
import {type CommonNavigatorParams} from '#/lib/routes/types'
@@ -52,7 +51,7 @@ export function ProfileFeedScreen(props: Props) {
const feedParams: FeedParams | undefined = props.route.params.feedCacheKey
? {feedCacheKey: props.route.params.feedCacheKey}
: undefined
const {_} = useLingui()
const {t: l} = useLingui()
const uri = useMemo(
() => makeRecordUri(handleOrDid, 'app.bsky.feed.generator', rkey),
@@ -70,7 +69,7 @@ export function ProfileFeedScreen(props: Props) {
<Layout.Screen testID="profileFeedScreenError">
<ErrorScreen
showHeader
title={_(msg`Could not load feed`)}
title={l`Could not load feed`}
message={cleanError(error)}
onPressTryAgain={() => void refetch()}
/>
@@ -131,7 +130,7 @@ export function ProfileFeedScreenInner({
feedInfo: FeedSourceFeedInfo
feedParams: FeedParams | undefined
}) {
const {_} = useLingui()
const {t: l} = useLingui()
const {hasSession} = useSession()
const {openComposer} = useOpenComposer()
const isScreenFocused = useIsFocused()
@@ -168,10 +167,10 @@ export function ProfileFeedScreenInner({
<EmptyState
icon={HashtagWideIcon}
iconSize="2xl"
message={_(msg`This feed is empty.`)}
message={l`This feed is empty.`}
/>
)
}, [_])
}, [l])
const isVideoFeed = useMemo(() => {
const isBskyVideoFeed = VIDEO_FEED_URIS.includes(feedInfo.uri)
@@ -181,13 +180,17 @@ export function ProfileFeedScreenInner({
return IS_NATIVE && _isVideoFeed
}, [feedInfo])
const isTrending =
feedInfo.creatorDid.toLowerCase() === TRENDING_DID ||
feedInfo.creatorHandle.toLowerCase() === TRENDING_HANDLE
return (
<>
<ProfileFeedHeader info={feedInfo} />
<ProfileFeedHeader info={feedInfo} isTrending={isTrending} />
<FeedFeedbackProvider value={feedFeedback}>
<PostFeed
enabled
description={isTrending ? feedInfo.description : undefined}
feed={feed}
feedParams={feedParams}
pollInterval={60e3}
@@ -199,22 +202,20 @@ export function ProfileFeedScreenInner({
isVideoFeed={isVideoFeed}
/>
</FeedFeedbackProvider>
{(isScrolledDown || hasNew) && (
<LoadLatestBtn
onPress={onScrollToTop}
label={_(msg`Load new posts`)}
label={l`Load new posts`}
showIndicator={hasNew}
/>
)}
{hasSession && (
<FAB
testID="composeFAB"
onPress={() => openComposer({logContext: 'Fab'})}
icon={<EditBigIcon size="lg" fill={t.palette.white} />}
accessibilityRole="button"
accessibilityLabel={_(msg`New post`)}
accessibilityLabel={l`New post`}
accessibilityHint=""
/>
)}
@@ -3,6 +3,7 @@ import {View} from 'react-native'
import {AtUri} from '@atproto/api'
import {Plural, Trans, useLingui} from '@lingui/react/macro'
import {TRENDING_HANDLE} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics'
import {makeCustomFeedLink, makeProfileLink} from '#/lib/routes/links'
import {shareUrl} from '#/lib/sharing'
@@ -24,20 +25,20 @@ import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {Divider} from '#/components/Divider'
import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {DotGrid3x1_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ShareIcon} from '#/components/icons/ArrowOutOfBox'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
import {DotGrid3x1_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/icons/DotGrid'
import {
Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled,
Heart2_Stroke2_Corner0_Rounded as Heart,
Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilledIcon,
Heart2_Stroke2_Corner0_Rounded as HeartIcon,
} from '#/components/icons/Heart2'
import {
Pin_Filled_Corner0_Rounded as PinFilled,
Pin_Stroke2_Corner0_Rounded as Pin,
Pin_Filled_Corner0_Rounded as PinFilledIcon,
Pin_Stroke2_Corner0_Rounded as PinIcon,
} from '#/components/icons/Pin'
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
import * as Layout from '#/components/Layout'
import {InlineLinkText} from '#/components/Link'
import * as Menu from '#/components/Menu'
@@ -74,14 +75,20 @@ export function ProfileFeedHeaderSkeleton() {
width: 34,
},
]}>
<Pin size="lg" fill={t.atoms.text_contrast_low.color} />
<PinIcon size="lg" fill={t.atoms.text_contrast_low.color} />
</View>
</Layout.Header.Slot>
</Layout.Header.Outer>
)
}
export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
export function ProfileFeedHeader({
info,
isTrending,
}: {
info: FeedSourceFeedInfo
isTrending: boolean
}) {
const t = useTheme()
const {t: l, i18n} = useLingui()
const ax = useAnalytics()
@@ -188,105 +195,143 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content align="left">
<Button
label={l`Open feed info screen`}
style={[
a.justify_start,
{
paddingVertical: IS_WEB ? 2 : 4,
paddingRight: 8,
},
]}
onPress={() => {
playHaptic()
infoControl.open()
}}>
{({hovered, pressed}) => (
<>
<View
{isTrending ? (
<View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
<View style={[a.flex_1]}>
<Text
style={[
a.absolute,
a.inset_0,
a.rounded_sm,
a.transition_all,
t.atoms.bg_contrast_25,
{
opacity: 0,
left: IS_WEB ? -2 : -4,
right: 0,
},
pressed && {
opacity: 1,
},
hovered && {
opacity: 1,
transform: [{scaleX: 1.01}, {scaleY: 1.1}],
},
a.text_md,
a.font_bold,
a.leading_snug,
gtMobile && a.text_lg,
]}
/>
numberOfLines={2}
emoji>
{info.displayName}
</Text>
</View>
<Button
label={l`Open feed info screen`}
size="medium"
shape="round"
color="secondary"
variant="ghost"
onPress={() => {
playHaptic()
infoControl.open()
}}>
<ButtonIcon icon={EllipsisIcon} />
</Button>
</View>
) : (
<Button
label={l`Open feed info screen`}
style={[
a.justify_start,
{
paddingVertical: IS_WEB ? 2 : 4,
paddingRight: 8,
},
]}
onPress={() => {
playHaptic()
infoControl.open()
}}>
{({hovered, pressed}) => (
<>
<View
style={[
a.absolute,
a.inset_0,
a.rounded_sm,
a.transition_all,
t.atoms.bg_contrast_25,
{
opacity: 0,
left: IS_WEB ? -2 : -4,
right: 0,
},
pressed && {
opacity: 1,
},
hovered && {
opacity: 1,
transform: [{scaleX: 1.01}, {scaleY: 1.1}],
},
]}
/>
<View
style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
{info.avatar && (
<UserAvatar size={36} type="algo" avatar={info.avatar} />
)}
<View
style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
{info.avatar && (
<UserAvatar
size={36}
type="algo"
avatar={info.avatar}
/>
)}
<View style={[a.flex_1]}>
<Text
style={[
a.text_md,
a.font_bold,
a.leading_snug,
gtMobile && a.text_lg,
]}
numberOfLines={2}
emoji>
{info.displayName}
</Text>
<View style={[a.flex_row, {gap: 6}]}>
<View style={[a.flex_1]}>
<Text
style={[
a.flex_shrink,
a.text_sm,
a.text_md,
a.font_bold,
a.leading_snug,
t.atoms.text_contrast_medium,
gtMobile && a.text_lg,
]}
numberOfLines={1}>
{sanitizeHandle(info.creatorHandle, '@')}
numberOfLines={2}
emoji>
{info.displayName}
</Text>
<View style={[a.flex_row, a.align_center, {gap: 2}]}>
<HeartFilled
size="xs"
fill={
likeUri
? t.palette.pink
: t.atoms.text_contrast_low.color
}
/>
<View style={[a.flex_row, a.gap_2xs]}>
<Text
style={[
a.flex_shrink,
a.text_sm,
a.leading_snug,
t.atoms.text_contrast_medium,
t.atoms.text_contrast_high,
]}
numberOfLines={1}>
{formatCount(i18n, likeCount)}
{sanitizeHandle(info.creatorHandle, '@')}
</Text>
{likeCount > 0 ? (
<View
style={[a.flex_row, a.align_center, {gap: 2}]}>
<HeartFilledIcon
size="xs"
fill={
likeUri
? t.palette.pink
: t.atoms.text_contrast_low.color
}
style={[{width: 14, height: 14}]}
/>
<Text
style={[
a.text_sm,
a.leading_snug,
t.atoms.text_contrast_high,
]}
numberOfLines={1}>
{formatCount(i18n, likeCount)}
</Text>
</View>
) : null}
</View>
</View>
</View>
<Ellipsis
size="md"
fill={t.atoms.text_contrast_low.color}
/>
</View>
</>
)}
</Button>
<EllipsisIcon
size="md"
fill={t.atoms.text_contrast_high.color}
/>
</View>
</>
)}
</Button>
)}
</Layout.Header.Content>
{hasSession && (
{!isTrending && hasSession ? (
<Layout.Header.Slot>
{isPinned ? (
<Menu.Root>
@@ -300,7 +345,10 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
variant="ghost"
shape="square"
color="secondary">
<PinFilled size="lg" fill={t.palette.primary_500} />
<PinFilledIcon
size="lg"
fill={t.palette.primary_500}
/>
</Button>
)
}}
@@ -310,23 +358,23 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
<Menu.Item
disabled={isFeedStateChangePending}
label={l`Unpin from home`}
onPress={onTogglePinned}>
onPress={() => void onTogglePinned()}>
<Menu.ItemText>{l`Unpin from home`}</Menu.ItemText>
<Menu.ItemIcon icon={X} position="right" />
<Menu.ItemIcon icon={XIcon} position="right" />
</Menu.Item>
<Menu.Item
disabled={isFeedStateChangePending}
label={
isSaved ? l`Remove from my feeds` : l`Save to my feeds`
}
onPress={onToggleSaved}>
onPress={() => void onToggleSaved()}>
<Menu.ItemText>
{isSaved
? l`Remove from my feeds`
: l`Save to my feeds`}
</Menu.ItemText>
<Menu.ItemIcon
icon={isSaved ? Trash : Plus}
icon={isSaved ? TrashIcon : PlusIcon}
position="right"
/>
</Menu.Item>
@@ -339,12 +387,12 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
variant="ghost"
shape="square"
color="secondary"
onPress={onTogglePinned}>
<ButtonIcon icon={Pin} size="lg" />
onPress={() => void onTogglePinned()}>
<ButtonIcon icon={PinIcon} size="lg" />
</Button>
)}
</Layout.Header.Slot>
)}
) : null}
</Layout.Header.Outer>
</Layout.Center>
<Dialog.Outer control={infoControl}>
@@ -358,7 +406,8 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
setLikeUri={setLikeUri}
likeCount={likeCount}
isPinned={isPinned}
onTogglePinned={onTogglePinned}
isTrending={isTrending}
onTogglePinned={() => void onTogglePinned()}
isFeedStateChangePending={isFeedStateChangePending}
/>
</Dialog.ScrollableInner>
@@ -373,6 +422,7 @@ function DialogInner({
setLikeUri,
likeCount,
isPinned,
isTrending,
onTogglePinned,
isFeedStateChangePending,
}: {
@@ -381,6 +431,7 @@ function DialogInner({
setLikeUri: (uri: string) => void
likeCount: number
isPinned: boolean
isTrending: boolean
onTogglePinned: () => void
isFeedStateChangePending: boolean
}) {
@@ -459,7 +510,9 @@ function DialogInner({
style={[a.text_sm, a.underline, t.atoms.text_contrast_medium]}
numberOfLines={1}
onPress={() => control.close()}>
{sanitizeHandle(info.creatorHandle, '@')}
{info.creatorHandle === TRENDING_HANDLE
? l`Bluesky`
: sanitizeHandle(info.creatorHandle, '@')}
</InlineLinkText>
</Trans>
</Text>
@@ -472,12 +525,13 @@ function DialogInner({
color="secondary"
shape="round"
onPress={onPressShare}>
<ButtonIcon icon={Share} size="lg" />
<ButtonIcon icon={ShareIcon} size="lg" />
</Button>
</View>
<RichText value={info.description} style={[a.text_md]} />
<View style={[a.flex_row, a.gap_sm, a.align_center]}>
{typeof likeCount === 'number' && (
{typeof likeCount === 'number' && likeCount > 0 ? (
<View style={[a.flex_row, a.gap_sm, a.align_center]}>
<InlineLinkText
label={l`View users who like this feed`}
to={makeCustomFeedLink(info.creatorDid, feedRkey, 'liked-by')}
@@ -487,41 +541,47 @@ function DialogInner({
Liked by <Plural value={likeCount} one="# user" other="# users" />
</Trans>
</InlineLinkText>
)}
</View>
{hasSession && (
</View>
) : null}
{hasSession ? (
<>
<View style={[a.flex_row, a.gap_sm, a.align_center, a.pt_sm]}>
<Button
disabled={isLikePending || isUnlikePending}
label={l`Like this feed`}
size="small"
color="secondary"
onPress={onToggleLiked}
style={[a.flex_1]}>
{isLiked ? (
<HeartFilled size="sm" fill={t.palette.pink} />
) : (
<ButtonIcon icon={Heart} />
)}
{!isTrending ? (
<View style={[a.flex_row, a.gap_sm, a.align_center, a.pt_sm]}>
<Button
disabled={isLikePending || isUnlikePending}
label={l`Like this feed`}
size="small"
color="secondary"
onPress={() => void onToggleLiked()}
style={[a.flex_1]}>
{isLiked ? (
<HeartFilledIcon size="sm" fill={t.palette.pink} />
) : (
<ButtonIcon icon={HeartIcon} />
)}
<ButtonText>
{isLiked ? <Trans>Unlike</Trans> : <Trans>Like</Trans>}
</ButtonText>
</Button>
<Button
disabled={isFeedStateChangePending}
label={isPinned ? l`Unpin feed` : l`Pin feed`}
size="small"
color={isPinned ? 'secondary' : 'primary'}
onPress={onTogglePinned}
style={[a.flex_1]}>
<ButtonText>
{isPinned ? <Trans>Unpin feed</Trans> : <Trans>Pin feed</Trans>}
</ButtonText>
<ButtonIcon icon={Pin} position="right" />
</Button>
</View>
<ButtonText>
{isLiked ? <Trans>Unlike</Trans> : <Trans>Like</Trans>}
</ButtonText>
</Button>
<Button
disabled={isFeedStateChangePending}
label={isPinned ? l`Unpin feed` : l`Pin feed`}
size="small"
color={isPinned ? 'secondary' : 'primary'}
onPress={onTogglePinned}
style={[a.flex_1]}>
<ButtonText>
{isPinned ? (
<Trans>Unpin feed</Trans>
) : (
<Trans>Pin feed</Trans>
)}
</ButtonText>
<ButtonIcon icon={PinIcon} position="right" />
</Button>
</View>
) : null}
<View style={[a.pt_xs, a.gap_lg]}>
<Divider />
@@ -541,7 +601,7 @@ function DialogInner({
<ButtonText>
<Trans>Report feed</Trans>
</ButtonText>
<ButtonIcon icon={CircleInfo} position="right" />
<ButtonIcon icon={CircleInfoIcon} position="right" />
</Button>
</View>
@@ -556,7 +616,7 @@ function DialogInner({
)}
</View>
</>
)}
) : null}
</View>
)
}
+1 -1
View File
@@ -327,7 +327,7 @@ export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
count += page.feeds.length
}
if (count < limit && (data?.pages.length || 0) < 6) {
query.fetchNextPage()
void query.fetchNextPage()
lastPageCountRef.current = data?.pages?.length || 0
}
}, [query, limit])
+35 -2
View File
@@ -17,6 +17,7 @@ import {
AppBskyEmbedImages,
AppBskyEmbedVideo,
type AppBskyFeedDefs,
type RichText as RichTextType,
} from '@atproto/api'
import {useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query'
@@ -50,7 +51,7 @@ import {List, type ListRef} from '#/view/com/util/List'
import {PostFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
import {type VideoFeedSourceContext} from '#/screens/VideoFeed/types'
import {useBreakpoints, useLayoutBreakpoints} from '#/alf'
import {atoms as a, useBreakpoints, useLayoutBreakpoints, useTheme} from '#/alf'
import {
AgeAssuranceDismissibleFeedBanner,
useInternalState as useAgeAssuranceBannerState,
@@ -64,6 +65,7 @@ import {FeedTrendingTopicsInterstitial} from '#/components/interstitials/FeedTre
import {TrendingInterstitial} from '#/components/interstitials/Trending'
import {TrendingVideos as TrendingVideosInterstitial} from '#/components/interstitials/TrendingVideos'
import {isStandardSiteEmbed} from '#/components/Post/Embed/StandardSiteEmbed/utils'
import {RichText} from '#/components/RichText'
import {useAnalytics} from '#/analytics'
import {IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
import {DiscoverFeedLiveEventFeedsAndTrendingBanner} from '#/features/liveEvents/components/DiscoverFeedLiveEventFeedsAndTrendingBanner'
@@ -105,6 +107,11 @@ type FeedRow =
type: 'fallbackMarker'
key: string
}
| {
type: 'description'
key: string
value: RichTextType
}
| {
type: 'sliceItem'
key: string
@@ -194,6 +201,7 @@ const CHECK_LATEST_AFTER = STALE.SECONDS.THIRTY
let PostFeed = ({
feed,
description,
feedParams,
ignoreFilterFor,
style,
@@ -216,6 +224,7 @@ let PostFeed = ({
isVideoFeed = false,
}: {
feed: FeedDescriptor
description?: RichTextType
feedParams?: FeedParams
ignoreFilterFor?: string
style?: StyleProp<ViewStyle>
@@ -232,13 +241,14 @@ let PostFeed = ({
progressViewOffset?: number
desktopFixedHeightOffset?: number
ListHeaderComponent?: () => React.ReactElement
extraData?: any
extraData?: Record<string, unknown>
savedFeedConfig?: AppBskyActorDefs.SavedFeed
initialNumToRender?: number
isVideoFeed?: boolean
lastFetchDate?: () => number
}): React.ReactNode => {
const ax = useAnalytics()
const t = useTheme()
const {t: l} = useLingui()
const queryClient = useQueryClient()
const {currentAccount, hasSession} = useSession()
@@ -389,6 +399,7 @@ let PostFeed = ({
* Cached value of whether the current feed was selected at startup. We don't
* want this to update when user swipes.
*/
// oxlint-disable-next-line react/hook-use-state
const [isCurrentFeedAtStartupSelected] = useState(selectedFeed === feed)
const blockedOrMutedAuthors = usePostAuthorShadowFilter(
@@ -680,8 +691,17 @@ let PostFeed = ({
}
}
if (description?.text) {
arr.unshift({
key: 'description',
type: 'description',
value: description,
})
}
return arr
}, [
description,
isFetched,
isError,
isEmpty,
@@ -793,6 +813,18 @@ let PostFeed = ({
return <PostFeedLoadingPlaceholder />
} else if (row.type === 'feedShutdownMsg') {
return <FeedShutdownMsg feedUri={feedUriOrActorDid} />
} else if (row.type === 'description') {
return (
<RichText
value={row.value}
style={[
a.m_md,
a.text_md,
a.leading_snug,
t.atoms.text_contrast_high,
]}
/>
)
} else if (row.type === 'interstitialFollows') {
return <SuggestedFollows feed={feed} />
} else if (row.type === 'interstitialProgressGuide') {
@@ -893,6 +925,7 @@ let PostFeed = ({
feedTab,
feedCacheKey,
onPressShowLess,
t,
],
)