Compare commits

...

14 Commits

Author SHA1 Message Date
Hailey d40ed2041b fix web double border 2025-04-07 16:06:00 -07:00
Hailey 329847df1e Update src/screens/Search/modules/ExploreInterestsCard.tsx 2025-04-07 15:30:40 -07:00
Hailey 0b8042d5a6 use a map 2025-04-07 15:29:51 -07:00
Eric Bailey c8904777ea Add PTR 2025-04-07 15:31:59 -05:00
Eric Bailey 3ed2aef7a6 Remove refetch on focus 2025-04-07 15:22:30 -05:00
Eric Bailey f4c41158a3 Update interests copy 2025-04-07 13:06:41 -05:00
Eric Bailey d1c3ae59ec Fetch 10 profiles 2025-04-07 12:20:53 -05:00
Eric Bailey 0a2a058681 Tighten up spacing 2025-04-07 12:18:44 -05:00
Eric Bailey ed4295f8d0 Add trending header if not top module 2025-04-07 11:50:25 -05:00
Eric Bailey 8da7e0ad8c Filter out pinned posts from feed previews 2025-04-07 11:06:28 -05:00
Eric Bailey 57ee0f61c2 Empty results state for sugg users 2025-04-07 10:44:11 -05:00
Eric Bailey b25aa7eda8 Use skeleton states for refetch on focus 2025-04-07 10:38:39 -05:00
Eric Bailey 67c79dd8ba Add subtle hover to all components except feed 2025-04-07 10:10:38 -05:00
Eric Bailey 2cc39b1458 Hover on sugg account 2025-04-07 09:39:39 -05:00
12 changed files with 344 additions and 190 deletions
+34
View File
@@ -0,0 +1,34 @@
import {View} from 'react-native'
import {atoms as a, useTheme, type ViewStyleProp} from '#/alf'
export function SubtleHover({style, hover}: ViewStyleProp & {hover: boolean}) {
const t = useTheme()
let opacity: number
switch (t.name) {
case 'dark':
opacity = 0.4
break
case 'dim':
opacity = 0.45
break
case 'light':
opacity = 0.5
break
}
return (
<View
style={[
a.absolute,
a.inset_0,
a.pointer_events_none,
a.transition_opacity,
t.atoms.bg_contrast_25,
style,
{opacity: hover ? opacity : 0},
]}
/>
)
}
+109 -38
View File
@@ -7,6 +7,7 @@ import {
} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {useGate} from '#/lib/statsig/statsig'
@@ -22,9 +23,19 @@ import {
import {useGetPopularFeedsQuery} from '#/state/queries/feed'
import {Nux, useNux} from '#/state/queries/nuxs'
import {usePreferencesQuery} from '#/state/queries/preferences'
import {useGetSuggestedFeedsQuery} from '#/state/queries/trending/useGetSuggestedFeedsQuery'
import {useGetSuggestedUsersQuery} from '#/state/queries/trending/useGetSuggestedUsersQuery'
import {useSuggestedStarterPacksQuery} from '#/state/queries/useSuggestedStarterPacksQuery'
import {
createGetSuggestedFeedsQueryKey,
useGetSuggestedFeedsQuery,
} from '#/state/queries/trending/useGetSuggestedFeedsQuery'
import {
getSuggestedUsersQueryKeyRoot,
useGetSuggestedUsersQuery,
} from '#/state/queries/trending/useGetSuggestedUsersQuery'
import {createGetTrendsQueryKey} from '#/state/queries/trending/useGetTrendsQuery'
import {
createSuggestedStarterPacksQueryKey,
useSuggestedStarterPacksQuery,
} from '#/state/queries/useSuggestedStarterPacksQuery'
import {useProgressGuide} from '#/state/shell/progress-guide'
import {isThreadChildAt, isThreadParentAt} from '#/view/com/posts/PostFeed'
import {PostFeedItem} from '#/view/com/posts/PostFeedItem'
@@ -41,6 +52,7 @@ import {ExploreRecommendations} from '#/screens/Search/modules/ExploreRecommenda
import {ExploreTrendingTopics} from '#/screens/Search/modules/ExploreTrendingTopics'
import {ExploreTrendingVideos} from '#/screens/Search/modules/ExploreTrendingVideos'
import {atoms as a, native, platform, useTheme, web} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button} from '#/components/Button'
import * as FeedCard from '#/components/FeedCard'
import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon} from '#/components/icons/Chevron'
@@ -49,9 +61,11 @@ import {type Props as IcoProps} from '#/components/icons/common'
import {type Props as SVGIconProps} from '#/components/icons/common'
import {ListSparkle_Stroke2_Corner0_Rounded as ListSparkle} from '#/components/icons/ListSparkle'
import {StarterPack} from '#/components/icons/StarterPack'
import {Trending2_Stroke2_Corner2_Rounded as Graph} from '#/components/icons/Trending'
import {UserCircle_Stroke2_Corner0_Rounded as Person} from '#/components/icons/UserCircle'
import {Loader} from '#/components/Loader'
import * as ProfileCard from '#/components/ProfileCard'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography'
import * as ModuleHeader from './components/ModuleHeader'
import {
@@ -69,33 +83,26 @@ function LoadMore({item}: {item: ExploreScreenItems & {type: 'loadMore'}}) {
onPress={item.onLoadMore}
style={[a.relative, a.w_full]}>
{({hovered, pressed}) => (
<View
style={[
a.flex_1,
a.flex_row,
a.align_center,
a.justify_center,
a.px_lg,
a.py_md,
a.gap_sm,
(hovered || pressed) && t.atoms.bg_contrast_25,
]}>
<Text
<>
<SubtleHover hover={hovered || pressed} />
<View
style={[
a.leading_snug,
hovered ? t.atoms.text : t.atoms.text_contrast_medium,
a.flex_1,
a.flex_row,
a.align_center,
a.justify_center,
a.px_lg,
a.py_md,
a.gap_sm,
]}>
{item.message}
</Text>
{item.isLoadingMore ? (
<Loader size="sm" />
) : (
<ChevronDownIcon
size="sm"
style={hovered ? t.atoms.text : t.atoms.text_contrast_medium}
/>
)}
</View>
<Text style={[a.leading_snug]}>{item.message}</Text>
{item.isLoadingMore ? (
<Loader size="sm" />
) : (
<ChevronDownIcon size="sm" style={t.atoms.text_contrast_medium} />
)}
</View>
</>
)}
</Button>
)
@@ -112,6 +119,7 @@ type ExploreScreenItems =
title: string
icon: React.ComponentType<SVGIconProps>
iconSize?: IcoProps['size']
bottomBorder?: boolean
searchButton?: {
label: string
metricsTag: MetricEvents['explore:module:searchButtonPress']['module']
@@ -147,6 +155,10 @@ type ExploreScreenItems =
profile: AppBskyActorDefs.ProfileViewBasic
recId?: number
}
| {
type: 'profileEmpty'
key: 'profileEmpty'
}
| {
type: 'feed'
key: string
@@ -203,11 +215,12 @@ export function Explore({
const gate = useGate()
const guide = useProgressGuide('follow-10')
const [selectedInterest, setSelectedInterest] = useState<string | null>(null)
// TODO always get at least 10 back
// TODO always get at least 10 back TODO still
const {
data: suggestedUsers,
isLoading: suggestedUsersIsLoading,
error: suggestedUsersError,
isRefetching: suggestedUsersIsRefetching,
} = useGetSuggestedUsersQuery({
category: selectedInterest,
})
@@ -227,6 +240,7 @@ export function Explore({
data: suggestedSPs,
isLoading: isLoadingSuggestedSPs,
error: suggestedSPsError,
isRefetching: isRefetchingSuggestedSPs,
} = useSuggestedStarterPacksQuery()
const isLoadingMoreFeeds = isFetchingNextFeedsPage && !isLoadingFeeds
@@ -262,6 +276,27 @@ export function Explore({
},
} = useFeedPreviews(suggestedFeeds?.feeds ?? [])
const qc = useQueryClient()
const [isPTR, setIsPTR] = useState(false)
const onPTR = useCallback(async () => {
setIsPTR(true)
await Promise.all([
await qc.resetQueries({
queryKey: createGetTrendsQueryKey(),
}),
await qc.resetQueries({
queryKey: createSuggestedStarterPacksQueryKey(),
}),
await qc.resetQueries({
queryKey: [getSuggestedUsersQueryKeyRoot],
}),
await qc.resetQueries({
queryKey: createGetSuggestedFeedsQueryKey(),
}),
])
setIsPTR(false)
}, [qc, setIsPTR])
const onLoadMoreFeedPreviews = useCallback(async () => {
if (
isPendingFeedPreviews ||
@@ -305,7 +340,7 @@ export function Explore({
},
})
if (suggestedUsersIsLoading) {
if (suggestedUsersIsLoading || suggestedUsersIsRefetching) {
i.push({type: 'profilePlaceholder', key: 'profilePlaceholder'})
} else if (suggestedUsersError) {
i.push({
@@ -333,14 +368,18 @@ export function Explore({
}
if (profileItems.length === 0) {
// no items! remove the header
i.pop()
i.push({
type: 'profileEmpty',
key: 'profileEmpty',
})
} else {
i.push(...profileItems)
}
} else {
// no items! remove the header
i.pop()
i.push({
type: 'profileEmpty',
key: 'profileEmpty',
})
}
} else {
i.push({type: 'profilePlaceholder', key: 'profilePlaceholder'})
@@ -352,6 +391,7 @@ export function Explore({
moderationOpts,
suggestedUsers,
suggestedUsersIsLoading,
suggestedUsersIsRefetching,
suggestedUsersError,
])
const suggestedFeedsModule = useMemo(() => {
@@ -466,7 +506,7 @@ export function Explore({
iconSize: 'xl',
})
if (isLoadingSuggestedSPs) {
if (isLoadingSuggestedSPs || isRefetchingSuggestedSPs) {
Array.from({length: 3}).forEach((__, index) =>
i.push({
type: 'starterPackSkeleton',
@@ -486,7 +526,13 @@ export function Explore({
})
}
return i
}, [suggestedSPs, _, isLoadingSuggestedSPs, suggestedSPsError])
}, [
suggestedSPs,
_,
isLoadingSuggestedSPs,
suggestedSPsError,
isRefetchingSuggestedSPs,
])
const feedPreviewsModule = useMemo(() => {
const i: ExploreScreenItems[] = []
i.push(...feedPreviewSlices)
@@ -520,6 +566,13 @@ export function Explore({
if (isNewUser) {
i.push(...suggestedFollowsModule)
i.push(...suggestedStarterPacksModule)
i.push({
type: 'header',
key: 'trending-topics-header',
title: _(msg`Trending topics`),
icon: Graph,
bottomBorder: true,
})
i.push(trendingTopicsModule)
} else {
i.push(trendingTopicsModule)
@@ -533,6 +586,7 @@ export function Explore({
return i
}, [
_,
topBorder,
isNewUser,
suggestedFollowsModule,
@@ -564,7 +618,7 @@ export function Explore({
)
case 'header': {
return (
<ModuleHeader.Container>
<ModuleHeader.Container bottomBorder={item.bottomBorder}>
<ModuleHeader.Icon icon={item.icon} size={item.iconSize} />
<ModuleHeader.TitleText>{item.title}</ModuleHeader.TitleText>
{item.searchButton && (
@@ -623,6 +677,15 @@ export function Explore({
/>
)
}
case 'profileEmpty': {
return (
<View style={[a.px_lg, a.pb_lg]}>
<Admonition>
<Trans>No results for "{selectedInterest}".</Trans>
</Admonition>
</View>
)
}
case 'feed': {
return (
<View
@@ -738,7 +801,13 @@ export function Explore({
return (
<ModuleHeader.Container
headerHeight={headerHeight}
style={[a.pt_xs, a.border_b, t.atoms.border_contrast_low]}>
style={[
a.pt_xs,
t.atoms.border_contrast_low,
native(a.border_b),
]}>
{/* Very non-scientific way to avoid small gap on scroll */}
<View style={[a.absolute, a.inset_0, t.atoms.bg, {top: -2}]} />
<ModuleHeader.FeedLink feed={item.feed}>
<ModuleHeader.FeedAvatar feed={item.feed} />
<View style={[a.flex_1, a.gap_xs]}>
@@ -876,6 +945,8 @@ export function Explore({
windowSize={9}
maxToRenderPerBatch={platform({ios: 5, default: 1})}
updateCellsBatchingPeriod={40}
refreshing={isPTR}
onRefresh={onPTR}
/>
)
}
@@ -18,7 +18,12 @@ export function Container({
style,
children,
headerHeight,
}: {children: React.ReactNode; headerHeight?: number} & ViewStyleProp) {
bottomBorder,
}: {
children: React.ReactNode
headerHeight?: number
bottomBorder?: boolean
} & ViewStyleProp) {
const t = useTheme()
return (
<View
@@ -31,10 +36,9 @@ export function Container({
a.gap_sm,
t.atoms.bg,
headerHeight && web({position: 'sticky', top: headerHeight}),
bottomBorder && [a.border_b, t.atoms.border_contrast_low],
style,
]}>
{/* Very non-scientific way to avoid small gap on scroll */}
<View style={[a.absolute, a.inset_0, t.atoms.bg, {top: -2}]} />
{children}
</View>
)
@@ -19,6 +19,7 @@ import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus
import {Link} from '#/components/Link'
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {useStarterPackLink} from '#/components/StarterPack/StarterPackCard'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography'
import * as bsky from '#/types/bsky'
@@ -48,75 +49,80 @@ export function StarterPackCard({
.map(item => item.subject)
return (
<View
style={[
a.w_full,
a.p_lg,
a.gap_md,
a.border,
a.rounded_sm,
a.overflow_hidden,
t.atoms.border_contrast_low,
]}>
<View aria-hidden style={[a.absolute, a.inset_0, a.z_40]}>
<Link
to={link.to}
label={link.label}
style={[a.absolute, a.inset_0]}
onHoverIn={link.precache}
onPress={link.precache}>
<View />
</Link>
</View>
<Link
to={link.to}
label={link.label}
onHoverIn={link.precache}
onPress={link.precache}>
{s => (
<>
<SubtleHover hover={s.hovered || s.pressed} />
<AvatarStack
profiles={profiles ?? []}
numPending={profileCount}
total={view.list?.listItemCount}
/>
<View
style={[
a.w_full,
a.p_lg,
a.gap_md,
a.border,
a.rounded_sm,
a.overflow_hidden,
t.atoms.border_contrast_low,
]}>
<AvatarStack
profiles={profiles ?? []}
numPending={profileCount}
total={view.list?.listItemCount}
/>
<View
style={[
a.w_full,
a.flex_row,
a.align_start,
a.gap_lg,
web({
position: 'static',
zIndex: 'unset',
}),
]}>
<View style={[a.flex_1]}>
<Text
emoji
style={[a.text_md, a.font_bold, a.leading_snug]}
numberOfLines={1}>
{view.record.name}
</Text>
<Text
emoji
style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}
numberOfLines={1}>
{view.creator?.did === currentAccount?.did
? _(msg`By you`)
: _(msg`By ${sanitizeHandle(view.creator.handle, '@')}`)}
</Text>
</View>
<Link
to={link.to}
label={link.label}
onHoverIn={link.precache}
onPress={link.precache}
variant="solid"
color="secondary"
size="small"
style={[a.z_50]}>
<ButtonText>
<Trans>Open pack</Trans>
</ButtonText>
</Link>
</View>
</View>
<View
style={[
a.w_full,
a.flex_row,
a.align_start,
a.gap_lg,
web({
position: 'static',
zIndex: 'unset',
}),
]}>
<View style={[a.flex_1]}>
<Text
emoji
style={[a.text_md, a.font_bold, a.leading_snug]}
numberOfLines={1}>
{view.record.name}
</Text>
<Text
emoji
style={[
a.text_sm,
a.leading_snug,
t.atoms.text_contrast_medium,
]}
numberOfLines={1}>
{view.creator?.did === currentAccount?.did
? _(msg`By you`)
: _(msg`By ${sanitizeHandle(view.creator.handle, '@')}`)}
</Text>
</View>
<Link
to={link.to}
label={link.label}
onHoverIn={link.precache}
onPress={link.precache}
variant="solid"
color="secondary"
size="small"
style={[a.z_50]}>
<ButtonText>
<Trans>Open pack</Trans>
</ButtonText>
</Link>
</View>
</View>
</>
)}
</Link>
)
}
@@ -40,14 +40,14 @@ export function ExploreInterestsCard() {
<>
<Prompt.Basic
control={trendingPrompt}
title={_(msg`Your interests`)}
title={_(msg`Dismiss interests`)}
description={_(
msg`You can adjust your interests at any time from your "Content and media" settings.`,
msg`You can adjust your interests at any time from "Content and media" settings.`,
)}
confirmButtonCta={_(
msg({
message: `Copy that!`,
comment: `Confirm button text. Can be a short cheeky phrase that means "OK" e.g. "Copy that!"`,
message: `OK`,
comment: `Confirm button text.`,
}),
)}
onConfirm={onConfirmClose}
@@ -17,6 +17,7 @@ import {atoms as a} from '#/alf'
import {Button} from '#/components/Button'
import * as ProfileCard from '#/components/ProfileCard'
import {boostInterests, Tabs} from '#/components/ProgressGuide/FollowDialog'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography'
import type * as bsky from '#/types/bsky'
@@ -133,10 +134,7 @@ let Tab = ({
a.py_sm,
a.border,
active || hovered || pressed || focused
? [
t.atoms.bg_contrast_25,
{borderColor: t.atoms.bg_contrast_25.backgroundColor},
]
? [t.atoms.bg_contrast_25, t.atoms.border_contrast_medium]
: [t.atoms.bg, t.atoms.border_contrast_low],
]}>
<Text
@@ -186,47 +184,52 @@ let SuggestedProfileCard = ({
{statsig: true},
)
}}>
<View
style={[
a.w_full,
a.py_lg,
a.px_lg,
a.border_t,
t.atoms.border_contrast_low,
a.flex_1,
]}>
<ProfileCard.Outer>
<ProfileCard.Header>
<ProfileCard.Avatar
profile={profile}
moderationOpts={moderationOpts}
/>
<ProfileCard.NameAndHandle
profile={profile}
moderationOpts={moderationOpts}
/>
<ProfileCard.FollowButton
profile={profile}
moderationOpts={moderationOpts}
withIcon={false}
logContext="ExploreSuggestedAccounts"
onFollow={() => {
logger.metric(
'suggestedUser:follow',
{
logContext: 'Explore',
location: 'Card',
recId,
position,
},
{statsig: true},
)
}}
/>
</ProfileCard.Header>
<ProfileCard.Description profile={profile} numberOfLines={2} />
</ProfileCard.Outer>
</View>
{s => (
<>
<SubtleHover hover={s.hovered || s.pressed} />
<View
style={[
a.flex_1,
a.w_full,
a.py_lg,
a.px_lg,
a.border_t,
t.atoms.border_contrast_low,
]}>
<ProfileCard.Outer>
<ProfileCard.Header>
<ProfileCard.Avatar
profile={profile}
moderationOpts={moderationOpts}
/>
<ProfileCard.NameAndHandle
profile={profile}
moderationOpts={moderationOpts}
/>
<ProfileCard.FollowButton
profile={profile}
moderationOpts={moderationOpts}
withIcon={false}
logContext="ExploreSuggestedAccounts"
onFollow={() => {
logger.metric(
'suggestedUser:follow',
{
logContext: 'Explore',
location: 'Card',
recId,
position,
},
{statsig: true},
)
}}
/>
</ProfileCard.Header>
<ProfileCard.Description profile={profile} numberOfLines={2} />
</ProfileCard.Outer>
</View>
</>
)}
</ProfileCard.Link>
)
}
@@ -17,6 +17,7 @@ import {type Props as SVGIconProps} from '#/components/icons/common'
import {Flame_Stroke2_Corner1_Rounded as FlameIcon} from '#/components/icons/Flame'
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
import {Link} from '#/components/Link'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography'
const TOPIC_COUNT = 5
@@ -28,10 +29,10 @@ export function ExploreTrendingTopics() {
}
function Inner() {
const {data: trending, error, isLoading} = useGetTrendsQuery()
const {data: trending, error, isLoading, isRefetching} = useGetTrendsQuery()
const noTopics = !isLoading && !error && !trending?.trends?.length
return isLoading ? (
return isLoading || isRefetching ? (
Array.from({length: TOPIC_COUNT}).map((__, i) => (
<TrendingTopicRowSkeleton key={i} withPosts={i === 0} />
))
@@ -92,25 +93,23 @@ export function TrendRow({
PressableComponent={Pressable}>
{({hovered, pressed}) => (
<>
<View
style={[
gutters,
a.w_full,
a.py_lg,
a.flex_row,
a.gap_2xs,
(hovered || pressed) && t.atoms.bg_contrast_25,
]}>
<SubtleHover hover={hovered || pressed} />
<View style={[gutters, a.w_full, a.py_lg, a.flex_row, a.gap_2xs]}>
<View style={[a.flex_1, a.gap_xs]}>
<View style={[a.flex_row]}>
<Text
style={[a.text_md, a.font_bold, a.leading_snug, {width: 20}]}>
style={[
a.text_md,
a.font_bold,
a.leading_tight,
{width: 20},
]}>
<Trans comment='The trending topic rank, i.e. "1. March Madness", "2. The Bachelor"'>
{rank}.
</Trans>
</Text>
<Text
style={[a.text_md, a.font_bold, a.leading_snug]}
style={[a.text_md, a.font_bold, a.leading_tight]}
numberOfLines={1}>
{trend.displayName}
</Text>
+53 -13
View File
@@ -34,6 +34,41 @@ const RQKEY_ROOT = 'feed-previews'
const RQKEY = (feeds: string[]) => [RQKEY_ROOT, feeds]
const LIMIT = 8 // sliced to 6, overfetch to account for moderation
const PINNED_POST_URIS: Record<string, boolean> = {
// 📰 News
'at://did:plc:kkf4naxqmweop7dv4l2iqqf5/app.bsky.feed.post/3lgh27w2ngc2b':
true,
// Gardening
'at://did:plc:5rw2on4i56btlcajojaxwcat/app.bsky.feed.post/3kjorckgcwc27':
true,
// Web Development Trending
'at://did:plc:m2sjv3wncvsasdapla35hzwj/app.bsky.feed.post/3lfaw445axs22':
true,
// Anime & Manga EN
'at://did:plc:tazrmeme4dzahimsykusrwrk/app.bsky.feed.post/3knxx2gmkns2y':
true,
// 📽️ Film
'at://did:plc:2hwwem55ce6djnk6bn62cstr/app.bsky.feed.post/3llhpzhbq7c2g':
true,
// PopSky
'at://did:plc:lfdf4srj43iwdng7jn35tjsp/app.bsky.feed.post/3lbblgly65c2g':
true,
// Science
'at://did:plc:hu2obebw3nhfj667522dahfg/app.bsky.feed.post/3kl33otd6ob2s':
true,
// Birds! 🦉
'at://did:plc:ffkgesg3jsv2j7aagkzrtcvt/app.bsky.feed.post/3lbg4r57yk22d':
true,
// Astronomy
'at://did:plc:xy2zorw2ys47poflotxthlzg/app.bsky.feed.post/3kyzye4lujs2w':
true,
// What's Cooking 🍽️
'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3lfqhgvxbqc2q':
true,
// BookSky 💙📚 #booksky
'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3kgrm2rw5ww2e':
true,
}
export type FeedPreviewItem =
| {
@@ -181,19 +216,24 @@ export function useFeedPreviews(
feedContext: item.feedContext,
reason: item.reason,
feedPostUri: item.feedPostUri,
items: item.items.slice(0, 6).map((subItem, i) => {
const feedPostSliceItem: FeedPostSliceItem = {
_reactKey: `${item._reactKey}-${i}-${subItem.post.uri}`,
uri: subItem.post.uri,
post: subItem.post,
record: subItem.record,
moderation: moderations[i],
parentAuthor: subItem.parentAuthor,
isParentBlocked: subItem.isParentBlocked,
isParentNotFound: subItem.isParentNotFound,
}
return feedPostSliceItem
}),
items: item.items
.slice(0, 6)
.filter(subItem => {
return !PINNED_POST_URIS[subItem.post.uri]
})
.map((subItem, i) => {
const feedPostSliceItem: FeedPostSliceItem = {
_reactKey: `${item._reactKey}-${i}-${subItem.post.uri}`,
uri: subItem.post.uri,
post: subItem.post,
record: subItem.record,
moderation: moderations[i],
parentAuthor: subItem.parentAuthor,
isParentBlocked: subItem.isParentBlocked,
isParentNotFound: subItem.isParentNotFound,
}
return feedPostSliceItem
}),
}
if (slice.isIncompleteThread && slice.items.length >= 3) {
const beforeLast = slice.items.length - 2
@@ -20,8 +20,7 @@ export function useGetSuggestedFeedsQuery() {
return useQuery({
enabled: !!preferences,
refetchOnWindowFocus: true,
staleTime: STALE.MINUTES.ONE,
staleTime: STALE.MINUTES.THREE,
queryKey: createGetSuggestedFeedsQueryKey(),
queryFn: async () => {
const contentLangs = getContentLanguages().join(',')
@@ -27,14 +27,14 @@ export function useGetSuggestedUsersQuery(props: QueryProps) {
return useQuery({
enabled: !!preferences,
refetchOnWindowFocus: true,
staleTime: STALE.MINUTES.ONE,
staleTime: STALE.MINUTES.THREE,
queryKey: createGetSuggestedUsersQueryKey(props),
queryFn: async () => {
const contentLangs = getContentLanguages().join(',')
const {data} = await agent.app.bsky.unspecced.getSuggestedUsers(
{
category: props.category ?? undefined,
limit: 10,
},
{
headers: {
@@ -25,7 +25,6 @@ export function useGetTrendsQuery() {
return useQuery({
enabled: !!preferences,
refetchOnWindowFocus: true,
staleTime: STALE.MINUTES.THREE,
queryKey: createGetTrendsQueryKey(),
queryFn: async () => {
@@ -20,8 +20,7 @@ export function useSuggestedStarterPacksQuery() {
return useQuery({
enabled: !!preferences,
refetchOnWindowFocus: true,
staleTime: STALE.MINUTES.ONE,
staleTime: STALE.MINUTES.THREE,
queryKey: createSuggestedStarterPacksQueryKey(),
async queryFn() {
const {data} = await agent.app.bsky.unspecced.getSuggestedStarterPacks(