Add pagination to feed search (#11475)

This commit is contained in:
DS Boyce
2026-08-17 09:55:45 -07:00
committed by GitHub
parent fd86a7d3d8
commit 01486d27e0
4 changed files with 74 additions and 22 deletions
-5
View File
@@ -1175,11 +1175,6 @@
"count": 1
}
},
"src/screens/StarterPack/Wizard/StepFeeds.tsx": {
"typescript/no-misused-promises": {
"count": 1
}
},
"src/screens/StarterPack/Wizard/index.tsx": {
"typescript/no-floating-promises": {
"count": 2
+27 -6
View File
@@ -648,26 +648,41 @@ let SearchScreenFeedsResults = ({
const ax = useAnalytics()
const t = useTheme()
const {data: results, isFetched} = usePopularFeedsSearch({
const {
data: results,
isFetched,
isFetching,
error,
fetchNextPage,
isFetchingNextPage,
hasNextPage,
} = usePopularFeedsSearch({
query,
enabled: active,
})
const feeds = useMemo(() => {
return results?.pages.flatMap(page => page.feeds) || []
}, [results])
const onEndReached = useCallback(() => {
if (isFetching || !hasNextPage || error) return
void fetchNextPage()
}, [isFetching, error, hasNextPage, fetchNextPage])
const fireTracking = useCallOnce(() => {
ax.metric('search:results:loaded', {
tab: 'feeds',
initialCount: results?.length ?? 0,
initialCount: feeds.length,
})
})
if (isFetched) {
fireTracking()
}
return isFetched && results ? (
return isFetched ? (
<>
{results.length ? (
{feeds.length || hasNextPage ? (
<List
data={results}
data={feeds}
renderItem={({
item,
index,
@@ -686,8 +701,14 @@ let SearchScreenFeedsResults = ({
</View>
)}
keyExtractor={(item: app.bsky.feed.defs.GeneratorView) => item.uri}
onEndReached={onEndReached}
desktopFixedHeight
ListFooterComponent={<ListFooter />}
ListFooterComponent={
<ListFooter
hasNextPage={hasNextPage}
isFetchingNextPage={isFetchingNextPage}
/>
}
/>
) : (
<EmptyState messageText={<NoResultsText query={query} />} />
+31 -4
View File
@@ -1,4 +1,4 @@
import {useState} from 'react'
import {useMemo, useState} from 'react'
import {type ListRenderItemInfo, View} from 'react-native'
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
import {type ModerationOpts} from '@bsky/sdk/moderation'
@@ -16,6 +16,7 @@ import {useWizardState} from '#/screens/StarterPack/Wizard/State'
import {atoms as a, useTheme} from '#/alf'
import {SearchInput} from '#/components/forms/SearchInput'
import {useThrottledValue} from '#/components/hooks/useThrottledValue'
import {ListFooter} from '#/components/Lists'
import {Loader} from '#/components/Loader'
import {ScreenTransition} from '#/components/ScreenTransition'
import {WizardFeedCard} from '#/components/StarterPack/Wizard/WizardListCard'
@@ -59,8 +60,16 @@ export function StepFeeds({moderationOpts}: {moderationOpts: ModerationOpts}) {
: savedFeeds
: undefined
const {data: searchedFeeds, isFetching: isFetchingSearchedFeeds} =
usePopularFeedsSearch({query: throttledQuery})
const {
data: searchedFeedsPages,
fetchNextPage: fetchNextSearchedFeedsPage,
hasNextPage: hasNextSearchedFeedsPage,
isFetching: isFetchingSearchedFeeds,
isFetchingNextPage: isFetchingNextSearchedFeedsPage,
} = usePopularFeedsSearch({query: throttledQuery})
const searchedFeeds = useMemo(() => {
return searchedFeedsPages?.pages.flatMap(page => page.feeds) ?? []
}, [searchedFeedsPages])
const isLoading =
!isFetchedSavedFeeds || isLoadingPopularFeeds || isFetchingSearchedFeeds
@@ -98,7 +107,17 @@ export function StepFeeds({moderationOpts}: {moderationOpts: ModerationOpts}) {
renderItem={renderItem}
keyExtractor={keyExtractor}
onEndReached={
!query && !screenReaderEnabled ? () => fetchNextPage() : undefined
!screenReaderEnabled
? () => {
if (query) {
if (hasNextSearchedFeedsPage) {
void fetchNextSearchedFeedsPage()
}
} else {
void fetchNextPage()
}
}
: undefined
}
onEndReachedThreshold={2}
keyboardDismissMode="on-drag"
@@ -107,6 +126,14 @@ export function StepFeeds({moderationOpts}: {moderationOpts: ModerationOpts}) {
disableFullWindowScroll={true}
sideBorders={false}
style={{flex: 1}}
ListFooterComponent={
query ? (
<ListFooter
hasNextPage={hasNextSearchedFeedsPage}
isFetchingNextPage={isFetchingNextSearchedFeedsPage}
/>
) : undefined
}
ListEmptyComponent={
<View style={[a.flex_1, a.align_center, a.mt_lg, a.px_lg]}>
{isLoading ? (
+16 -7
View File
@@ -380,26 +380,35 @@ export function usePopularFeedsSearch({
const moderationOpts = useModerationOpts()
const enabledInner = enabled ?? Boolean(moderationOpts)
return useQuery({
return useInfiniteQuery({
enabled: enabledInner,
queryKey: createPopularFeedsSearchQueryKey(query),
queryFn: async () => {
queryFn: async ({pageParam}) => {
const data = await client.call(
app.bsky.unspecced.getPopularFeedGenerators,
{
limit: 15,
query: query,
cursor: pageParam,
},
)
return data.feeds
return data
},
initialPageParam: undefined as string | undefined,
getNextPageParam: lastPage => lastPage.cursor,
placeholderData: keepPreviousData,
select(data) {
return data.filter(feed => {
const decision = moderateFeedGenerator(feed, moderationOpts!)
return !decision.ui('contentMedia').blur
})
return {
...data,
pages: data.pages.map(page => ({
...page,
feeds: page.feeds.filter(feed => {
const decision = moderateFeedGenerator(feed, moderationOpts!)
return !decision.ui('contentMedia').blur
}),
})),
}
},
})
}