Search lists cleanup, pagninate actor search (#9009)

This commit is contained in:
Samuel Newman
2025-11-20 17:59:20 +02:00
committed by GitHub
parent 2792b4cef0
commit 2919a7da7f
7 changed files with 142 additions and 84 deletions
+2 -2
View File
@@ -17,7 +17,7 @@ import {logger} from '#/logger'
import {type MetricEvents} from '#/logger/metrics'
import {useLanguagePrefs} from '#/state/preferences/languages'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {RQKEY_ROOT_PAGINATED as useActorSearchPaginatedQueryKeyRoot} from '#/state/queries/actor-search'
import {RQKEY_ROOT as useActorSearchQueryKeyRoot} from '#/state/queries/actor-search'
import {
type FeedPreviewItem,
useFeedPreviews,
@@ -308,7 +308,7 @@ export function Explore({
queryKey: [getSuggestedUsersQueryKeyRoot],
}),
qc.resetQueries({
queryKey: [useActorSearchPaginatedQueryKeyRoot],
queryKey: [useActorSearchQueryKeyRoot],
}),
qc.resetQueries({
queryKey: createGetSuggestedFeedsQueryKey(),
+106 -24
View File
@@ -4,7 +4,8 @@ import {type AppBskyFeedDefs} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {usePalette} from '#/lib/hooks/usePalette'
import {urls} from '#/lib/constants'
import {cleanError} from '#/lib/strings/errors'
import {augmentSearchQuery} from '#/lib/strings/helpers'
import {useActorSearch} from '#/state/queries/actor-search'
import {usePopularFeedsSearch} from '#/state/queries/feed'
@@ -21,6 +22,7 @@ import {atoms as a, useTheme, web} from '#/alf'
import * as FeedCard from '#/components/FeedCard'
import * as Layout from '#/components/Layout'
import {InlineLinkText} from '#/components/Link'
import {ListFooter} from '#/components/Lists'
import {SearchError} from '#/components/SearchError'
import {Text} from '#/components/Typography'
@@ -112,11 +114,11 @@ function Loader() {
}
function EmptyState({
message,
messageText,
error,
children,
}: {
message: string
messageText: React.ReactNode
error?: string
children?: React.ReactNode
}) {
@@ -126,7 +128,7 @@ function EmptyState({
<Layout.Content>
<View style={[a.p_xl]}>
<View style={[t.atoms.bg_contrast_25, a.rounded_sm, a.p_lg]}>
<Text style={[a.text_md]}>{message}</Text>
<Text style={[a.text_md]}>{messageText}</Text>
{error && (
<>
@@ -155,6 +157,41 @@ function EmptyState({
)
}
function NoResultsText({query}: {query: string}) {
const t = useTheme()
const {_} = useLingui()
return (
<>
<Text style={[a.text_lg, t.atoms.text_contrast_high]}>
<Trans>
No results found for "
<Text style={[a.text_lg, t.atoms.text, a.font_medium]}>{query}</Text>
".
</Trans>
</Text>
{'\n\n'}
<Text style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high]}>
<Trans context="english-only-resource">
Try a different search term, or{' '}
<InlineLinkText
label={_(
msg({
message: 'read about how to use search filters',
context: 'english-only-resource',
}),
)}
to={urls.website.blog.searchTipsAndTricks}
style={[a.text_md, a.leading_snug]}>
read about how to use search filters
</InlineLinkText>
.
</Trans>
</Text>
</>
)
}
type SearchResultSlice =
| {
type: 'post'
@@ -176,9 +213,8 @@ let SearchScreenPostResults = ({
active: boolean
}): React.ReactNode => {
const {_} = useLingui()
const {currentAccount} = useSession()
const {currentAccount, hasSession} = useSession()
const [isPTR, setIsPTR] = useState(false)
const isLoggedin = Boolean(currentAccount?.did)
const augmentedQuery = useMemo(() => {
return augmentSearchQuery(query || '', {did: currentAccount?.did})
@@ -195,7 +231,6 @@ let SearchScreenPostResults = ({
hasNextPage,
} = useSearchPostsQuery({query: augmentedQuery, sort, enabled: active})
const pal = usePalette('default')
const t = useTheme()
const onPullToRefresh = useCallback(async () => {
setIsPTR(true)
@@ -249,14 +284,13 @@ let SearchScreenPostResults = ({
requestSwitchToAccount({requestedAccount: 'new'})
}
if (!isLoggedin) {
if (!hasSession) {
return (
<SearchError
title={_(msg`Search is currently unavailable when logged out`)}>
<Text style={[a.text_md, a.text_center, a.leading_snug]}>
<Trans>
<InlineLinkText
style={[pal.link]}
label={_(msg`Sign in`)}
to={'#'}
onPress={showSignIn}>
@@ -264,7 +298,6 @@ let SearchScreenPostResults = ({
</InlineLinkText>
<Text style={t.atoms.text_contrast_medium}> or </Text>
<InlineLinkText
style={[pal.link]}
label={_(msg`Create an account`)}
to={'#'}
onPress={showCreateAccount}>
@@ -283,10 +316,10 @@ let SearchScreenPostResults = ({
return error ? (
<EmptyState
message={_(
messageText={_(
msg`We're sorry, but your search could not be completed. Please try again in a few minutes.`,
)}
error={error.toString()}
error={cleanError(error)}
/>
) : (
<>
@@ -307,10 +340,15 @@ let SearchScreenPostResults = ({
onRefresh={onPullToRefresh}
onEndReached={onEndReached}
desktopFixedHeight
contentContainerStyle={{paddingBottom: 100}}
ListFooterComponent={
<ListFooter
isFetchingNextPage={isFetchingNextPage}
hasNextPage={hasNextPage}
/>
}
/>
) : (
<EmptyState message={_(msg`No results found for ${query}`)} />
<EmptyState messageText={<NoResultsText query={query} />} />
)}
</>
) : (
@@ -329,24 +367,69 @@ let SearchScreenUserResults = ({
active: boolean
}): React.ReactNode => {
const {_} = useLingui()
const {hasSession} = useSession()
const [isPTR, setIsPTR] = useState(false)
const {data: results, isFetched} = useActorSearch({
const {
isFetched,
data: results,
isFetching,
error,
refetch,
fetchNextPage,
isFetchingNextPage,
hasNextPage,
} = useActorSearch({
query,
enabled: active,
})
return isFetched && results ? (
const onPullToRefresh = useCallback(async () => {
setIsPTR(true)
await refetch()
setIsPTR(false)
}, [setIsPTR, refetch])
const onEndReached = useCallback(() => {
if (!hasSession) return
if (isFetching || !hasNextPage || error) return
fetchNextPage()
}, [isFetching, error, hasNextPage, fetchNextPage, hasSession])
const profiles = useMemo(() => {
return results?.pages.flatMap(page => page.actors) || []
}, [results])
if (error) {
return (
<EmptyState
messageText={_(
msg`We're sorry, but your search could not be completed. Please try again in a few minutes.`,
)}
error={error.toString()}
/>
)
}
return isFetched && profiles ? (
<>
{results.length ? (
{profiles.length ? (
<List
data={results}
data={profiles}
renderItem={({item}) => <ProfileCardWithFollowBtn profile={item} />}
keyExtractor={item => item.did}
refreshing={isPTR}
onRefresh={onPullToRefresh}
onEndReached={onEndReached}
desktopFixedHeight
contentContainerStyle={{paddingBottom: 100}}
ListFooterComponent={
<ListFooter
hasNextPage={hasNextPage && hasSession}
isFetchingNextPage={isFetchingNextPage}
/>
}
/>
) : (
<EmptyState message={_(msg`No results found for ${query}`)} />
<EmptyState messageText={<NoResultsText query={query} />} />
)}
</>
) : (
@@ -363,7 +446,6 @@ let SearchScreenFeedsResults = ({
active: boolean
}): React.ReactNode => {
const t = useTheme()
const {_} = useLingui()
const {data: results, isFetched} = usePopularFeedsSearch({
query,
@@ -378,7 +460,7 @@ let SearchScreenFeedsResults = ({
renderItem={({item}) => (
<View
style={[
a.border_b,
a.border_t,
t.atoms.border_contrast_low,
a.px_lg,
a.py_lg,
@@ -388,10 +470,10 @@ let SearchScreenFeedsResults = ({
)}
keyExtractor={item => item.uri}
desktopFixedHeight
contentContainerStyle={{paddingBottom: 100}}
ListFooterComponent={<ListFooter />}
/>
) : (
<EmptyState message={_(msg`No results found for ${query}`)} />
<EmptyState messageText={<NoResultsText query={query} />} />
)}
</>
) : (
+2 -2
View File
@@ -1,7 +1,7 @@
import {useMemo} from 'react'
import {useInterestsDisplayNames} from '#/lib/interests'
import {useActorSearchPaginated} from '#/state/queries/actor-search'
import {useActorSearch} from '#/state/queries/actor-search'
import {useGetSuggestedUsersQuery} from '#/state/queries/trending/useGetSuggestedUsersQuery'
/**
@@ -31,7 +31,7 @@ export function useSuggestedUsers({
category,
overrideInterests,
})
const searched = useActorSearchPaginated({
const searched = useActorSearch({
enabled: !!search,
// use user's app language translation for this value
query: category ? interestsDisplayNames[category] : '',