Migrate all search to v2 and remove v1 (#11125)
This commit is contained in:
+25
-24
@@ -1,9 +1,7 @@
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {type ListRenderItemInfo, View} from 'react-native'
|
||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
|
||||
import {HITSLOP_10} from '#/lib/constants'
|
||||
@@ -14,7 +12,7 @@ import {shareUrl} from '#/lib/sharing'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {enforceLen} from '#/lib/strings/helpers'
|
||||
import {useSearchPostsQuery} from '#/state/queries/search-posts'
|
||||
import {useSearchPostsV2Query} from '#/state/queries/search-posts-v2'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||
import {useCloseAllActiveElements} from '#/state/util'
|
||||
@@ -43,7 +41,7 @@ export default function HashtagScreen({
|
||||
route,
|
||||
}: NativeStackScreenProps<CommonNavigatorParams, 'Hashtag'>) {
|
||||
const {tag, author} = route.params
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
|
||||
const decodedTag = useMemo(() => {
|
||||
return decodeURIComponent(tag)
|
||||
@@ -73,7 +71,7 @@ export default function HashtagScreen({
|
||||
if (author) {
|
||||
url.searchParams.set('author', author)
|
||||
}
|
||||
shareUrl(url.toString())
|
||||
void shareUrl(url.toString())
|
||||
}, [tag, author])
|
||||
|
||||
const [activeTab, setActiveTab] = useState(0)
|
||||
@@ -85,7 +83,7 @@ export default function HashtagScreen({
|
||||
const sections = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
title: _(msg`Top`),
|
||||
title: l`Top`,
|
||||
component: (
|
||||
<HashtagScreenTab
|
||||
fullTag={fullTag}
|
||||
@@ -96,7 +94,7 @@ export default function HashtagScreen({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: _(msg`Latest`),
|
||||
title: l`Latest`,
|
||||
component: (
|
||||
<HashtagScreenTab
|
||||
fullTag={fullTag}
|
||||
@@ -107,7 +105,7 @@ export default function HashtagScreen({
|
||||
),
|
||||
},
|
||||
]
|
||||
}, [_, fullTag, author, activeTab])
|
||||
}, [l, fullTag, author, activeTab])
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
@@ -122,14 +120,14 @@ export default function HashtagScreen({
|
||||
{author && (
|
||||
<Layout.Header.SubtitleText>
|
||||
{author.startsWith('did:')
|
||||
? _(msg`From ${sanitizedAuthor}`)
|
||||
: _(msg`From @${sanitizedAuthor}`)}
|
||||
? l`From ${sanitizedAuthor}`
|
||||
: l`From @${sanitizedAuthor}`}
|
||||
</Layout.Header.SubtitleText>
|
||||
)}
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot>
|
||||
<Button
|
||||
label={_(msg`Share`)}
|
||||
label={l`Share`}
|
||||
size="small"
|
||||
variant="ghost"
|
||||
color="primary"
|
||||
@@ -164,7 +162,7 @@ function HashtagScreenTab({
|
||||
sort: 'top' | 'latest'
|
||||
active: boolean
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const initialNumToRender = useInitialNumToRender()
|
||||
const [isPTR, setIsPTR] = useState(false)
|
||||
const t = useTheme()
|
||||
@@ -188,7 +186,14 @@ function HashtagScreenTab({
|
||||
refetch,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
} = useSearchPostsQuery({query: queryParam, sort, enabled: active, author})
|
||||
} = useSearchPostsV2Query({
|
||||
query: queryParam,
|
||||
sort,
|
||||
enabled: active,
|
||||
filters: {
|
||||
author,
|
||||
},
|
||||
})
|
||||
|
||||
const posts = useMemo(() => {
|
||||
return data?.pages.flatMap(page => page.posts) || []
|
||||
@@ -202,7 +207,7 @@ function HashtagScreenTab({
|
||||
|
||||
const onEndReached = useCallback(() => {
|
||||
if (isFetchingNextPage || !hasNextPage || error) return
|
||||
fetchNextPage()
|
||||
void fetchNextPage()
|
||||
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
|
||||
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
@@ -220,19 +225,15 @@ function HashtagScreenTab({
|
||||
|
||||
if (!hasSession) {
|
||||
return (
|
||||
<SearchError
|
||||
title={_(msg`Search is currently unavailable when logged out`)}>
|
||||
<SearchError title={l`Search is currently unavailable when logged out`}>
|
||||
<Text style={[a.text_md, a.text_center, a.leading_snug]}>
|
||||
<Trans>
|
||||
<InlineLinkText
|
||||
label={_(msg`Sign in`)}
|
||||
to={'#'}
|
||||
onPress={showSignIn}>
|
||||
<InlineLinkText label={l`Sign in`} to={'#'} onPress={showSignIn}>
|
||||
Sign in
|
||||
</InlineLinkText>
|
||||
<Text style={t.atoms.text_contrast_medium}> or </Text>
|
||||
<InlineLinkText
|
||||
label={_(msg`Create an account`)}
|
||||
label={l`Create an account`}
|
||||
to={'#'}
|
||||
onPress={showCreateAccount}>
|
||||
create an account
|
||||
@@ -256,7 +257,7 @@ function HashtagScreenTab({
|
||||
isError={isError}
|
||||
onRetry={refetch}
|
||||
emptyType="results"
|
||||
emptyMessage={_(msg`We couldn't find any results for that tag.`)}
|
||||
emptyMessage={l`We couldn't find any results for that tag.`}
|
||||
/>
|
||||
) : (
|
||||
<List
|
||||
@@ -264,7 +265,7 @@ function HashtagScreenTab({
|
||||
renderItem={renderItem}
|
||||
keyExtractor={keyExtractor}
|
||||
refreshing={isPTR}
|
||||
onRefresh={onRefresh}
|
||||
onRefresh={() => void onRefresh()}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={4}
|
||||
onItemSeen={trackPostView}
|
||||
|
||||
@@ -10,7 +10,6 @@ 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'
|
||||
import {useSearchPostsQuery} from '#/state/queries/search-posts'
|
||||
import {useSearchPostsV2Query} from '#/state/queries/search-posts-v2'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||
@@ -36,7 +35,6 @@ import type * as bsky from '#/types/bsky'
|
||||
|
||||
let SearchResults = ({
|
||||
query,
|
||||
queryWithParams,
|
||||
filters,
|
||||
hasFilters,
|
||||
activeTab,
|
||||
@@ -44,7 +42,6 @@ let SearchResults = ({
|
||||
headerHeight,
|
||||
}: {
|
||||
query: string
|
||||
queryWithParams: string
|
||||
filters: SearchFilters
|
||||
hasFilters: boolean
|
||||
activeTab: number
|
||||
@@ -75,7 +72,6 @@ let SearchResults = ({
|
||||
<SearchScreenPostResults
|
||||
hasFilters={hasFilters}
|
||||
query={query}
|
||||
queryWithParams={queryWithParams}
|
||||
filters={filters}
|
||||
sort="top"
|
||||
active={activePage === 0}
|
||||
@@ -88,7 +84,6 @@ let SearchResults = ({
|
||||
<SearchScreenPostResults
|
||||
hasFilters={hasFilters}
|
||||
query={query}
|
||||
queryWithParams={queryWithParams}
|
||||
filters={filters}
|
||||
sort="latest"
|
||||
active={activePage === 1}
|
||||
@@ -111,15 +106,7 @@ let SearchResults = ({
|
||||
title: string
|
||||
component: React.ReactNode
|
||||
}[]
|
||||
}, [
|
||||
l,
|
||||
query,
|
||||
queryWithParams,
|
||||
filters,
|
||||
hasFilters,
|
||||
hasPostFilters,
|
||||
activePage,
|
||||
])
|
||||
}, [l, query, filters, hasFilters, hasPostFilters, activePage])
|
||||
|
||||
// There may be fewer tabs after changing the search options.
|
||||
const selectedPage = activePage > sections.length - 1 ? 0 : activePage
|
||||
@@ -204,14 +191,9 @@ function NoResultsText({
|
||||
hasFilters?: boolean
|
||||
query: string
|
||||
}) {
|
||||
const ax = useAnalytics()
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
|
||||
const searchV2Enabled = ax.features.enabled(ax.features.SearchV2Enable)
|
||||
const advancedSearchV2Enabled =
|
||||
searchV2Enabled && ax.features.enabled(ax.features.AdvancedSearchV2Enable)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text style={[a.text_lg, t.atoms.text_contrast_high]}>
|
||||
@@ -257,35 +239,19 @@ function NoResultsText({
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_high,
|
||||
]}>
|
||||
{advancedSearchV2Enabled ? (
|
||||
<Trans context="english-only-resource">
|
||||
Learn more about{' '}
|
||||
<InlineLinkText
|
||||
label={l({
|
||||
message: 'Read about how to use advanced search filters',
|
||||
context: 'english-only-resource',
|
||||
})}
|
||||
to={urls.website.blog.searchTipsAndTricks}
|
||||
style={[a.text_md, a.leading_snug]}>
|
||||
how to use advanced search
|
||||
</InlineLinkText>
|
||||
.
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans context="english-only-resource">
|
||||
Learn more about{' '}
|
||||
<InlineLinkText
|
||||
label={l({
|
||||
message: 'Read about how to use search filters',
|
||||
context: 'english-only-resource',
|
||||
})}
|
||||
to={urls.website.blog.searchTipsAndTricks}
|
||||
style={[a.text_md, a.leading_snug]}>
|
||||
how to use search filters
|
||||
</InlineLinkText>
|
||||
.
|
||||
</Trans>
|
||||
)}
|
||||
<Trans context="english-only-resource">
|
||||
Learn more about{' '}
|
||||
<InlineLinkText
|
||||
label={l({
|
||||
message: 'Read about how to use advanced search filters',
|
||||
context: 'english-only-resource',
|
||||
})}
|
||||
to={urls.website.blog.searchTipsAndTricks}
|
||||
style={[a.text_md, a.leading_snug]}>
|
||||
how to use advanced search
|
||||
</InlineLinkText>
|
||||
.
|
||||
</Trans>
|
||||
</Text>
|
||||
</>
|
||||
)
|
||||
@@ -305,14 +271,12 @@ type SearchResultSlice =
|
||||
let SearchScreenPostResults = ({
|
||||
hasFilters = false,
|
||||
query,
|
||||
queryWithParams,
|
||||
filters,
|
||||
sort,
|
||||
active,
|
||||
}: {
|
||||
hasFilters: boolean
|
||||
query: string
|
||||
queryWithParams: string
|
||||
filters?: SearchFilters
|
||||
sort?: 'top' | 'latest'
|
||||
active: boolean
|
||||
@@ -323,30 +287,15 @@ let SearchScreenPostResults = ({
|
||||
const [isPTR, setIsPTR] = useState(false)
|
||||
const trackPostView = usePostViewTracking('SearchResults')
|
||||
|
||||
const searchV2Enabled = ax.features.enabled(ax.features.SearchV2Enable)
|
||||
|
||||
const augmentedV2Query = useMemo(() => {
|
||||
return augmentSearchQuery(query || '')
|
||||
}, [query])
|
||||
const augmentedV1Query = useMemo(() => {
|
||||
return augmentSearchQuery(queryWithParams || '')
|
||||
}, [queryWithParams])
|
||||
|
||||
/*
|
||||
* Both hooks are called to keep hook order stable; `enabled` ensures only the
|
||||
* gated one actually fetches. V2 sends structured `filters` as separate
|
||||
* params, V1 keeps the existing single-`q` behavior.
|
||||
*/
|
||||
const v1 = useSearchPostsQuery({
|
||||
query: augmentedV1Query,
|
||||
sort,
|
||||
enabled: active && !searchV2Enabled,
|
||||
})
|
||||
const v2 = useSearchPostsV2Query({
|
||||
query: augmentedV2Query,
|
||||
filters,
|
||||
sort,
|
||||
enabled: active && searchV2Enabled,
|
||||
enabled: active,
|
||||
})
|
||||
const {
|
||||
isFetched,
|
||||
@@ -357,7 +306,7 @@ let SearchScreenPostResults = ({
|
||||
fetchNextPage,
|
||||
isFetchingNextPage,
|
||||
hasNextPage,
|
||||
} = searchV2Enabled ? v2 : v1
|
||||
} = v2
|
||||
|
||||
const t = useTheme()
|
||||
const onPullToRefresh = useCallback(async () => {
|
||||
|
||||
@@ -36,7 +36,6 @@ import {useSession} from '#/state/session'
|
||||
import {
|
||||
countActiveFilters,
|
||||
definedFilterParams,
|
||||
filtersToLegacyParams,
|
||||
filtersToRouteParams,
|
||||
hasActiveFilters,
|
||||
parseHistoryEntry,
|
||||
@@ -45,7 +44,6 @@ import {
|
||||
serializeHistoryEntry,
|
||||
withoutFilterParams,
|
||||
} from '#/screens/Search/searchParams'
|
||||
import {makeSearchQuery} from '#/screens/Search/utils'
|
||||
import {
|
||||
atoms as a,
|
||||
native,
|
||||
@@ -71,7 +69,6 @@ import {AutocompleteResults} from './components/AutocompleteResults'
|
||||
import {DetectedLanguagesAdmonition} from './components/DetectedLanguagesAdmonition'
|
||||
import {SearchAutocompleteInput} from './components/SearchAutocompleteInput'
|
||||
import {SearchHistory} from './components/SearchHistory'
|
||||
import {SearchLanguageDropdown} from './components/SearchLanguageDropdown'
|
||||
import {Explore} from './Explore'
|
||||
import {SearchResults} from './SearchResults'
|
||||
|
||||
@@ -122,10 +119,6 @@ export function SearchScreenShell({
|
||||
const {currentAccount} = useSession()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const searchV2Enabled = ax.features.enabled(ax.features.SearchV2Enable)
|
||||
const advancedSearchV2Enabled =
|
||||
searchV2Enabled && ax.features.enabled(ax.features.AdvancedSearchV2Enable)
|
||||
|
||||
// Get tab parameter from route params
|
||||
const tabParam = (route.params as {q?: string; tab?: TabParam})?.tab
|
||||
const [activeTab, setActiveTab] = useState(() => getTabIndex(tabParam))
|
||||
@@ -207,11 +200,10 @@ export function SearchScreenShell({
|
||||
[accountHistory, setAccountHistory],
|
||||
)
|
||||
|
||||
const {query, queryWithParams, filters, setFilters, hasFilters} =
|
||||
useQueryManager({
|
||||
initialQuery: queryParam,
|
||||
fixedParams,
|
||||
})
|
||||
const {query, filters, setFilters, hasFilters} = useQueryManager({
|
||||
initialQuery: queryParam,
|
||||
fixedParams,
|
||||
})
|
||||
const showFilters = Boolean((query || hasFilters) && !showAutocomplete)
|
||||
|
||||
const onChangeLang = useCallback(
|
||||
@@ -560,12 +552,7 @@ export function SearchScreenShell({
|
||||
{isExplore ? <Trans>Explore</Trans> : <Trans>Search</Trans>}
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
{showFilters && !advancedSearchV2Enabled ? (
|
||||
<SearchLanguageDropdown
|
||||
value={filters.lang ?? ''}
|
||||
onChange={onChangeLang}
|
||||
/>
|
||||
) : showFilters && advancedSearchV2Enabled ? (
|
||||
{showFilters ? (
|
||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||
<AdvancedSearchDialog
|
||||
disabled={activeTab > 1}
|
||||
@@ -591,7 +578,7 @@ export function SearchScreenShell({
|
||||
)}
|
||||
<View style={[a.px_lg, a.pt_sm, a.pb_sm, a.overflow_hidden]}>
|
||||
<View style={[a.gap_sm]}>
|
||||
{searchV2Enabled && query && !showAutocomplete && (
|
||||
{query && !showAutocomplete && (
|
||||
<DetectedLanguagesAdmonition
|
||||
query={query}
|
||||
filters={filters}
|
||||
@@ -635,7 +622,7 @@ export function SearchScreenShell({
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
{showFilters && !showHeader && advancedSearchV2Enabled ? (
|
||||
{showFilters && !showHeader ? (
|
||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||
<AdvancedSearchDialog
|
||||
disabled={activeTab > 1}
|
||||
@@ -655,15 +642,6 @@ export function SearchScreenShell({
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{showFilters && !showHeader && !advancedSearchV2Enabled && (
|
||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||
<SearchLanguageDropdown
|
||||
value={filters.lang ?? ''}
|
||||
onChange={onChangeLang}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Layout.Center>
|
||||
@@ -676,7 +654,6 @@ export function SearchScreenShell({
|
||||
activeTab={activeTab}
|
||||
setActiveTab={setActiveTab}
|
||||
query={query}
|
||||
queryWithParams={queryWithParams}
|
||||
filters={filters}
|
||||
hasFilters={hasFilters}
|
||||
headerHeight={headerHeight}
|
||||
@@ -728,7 +705,6 @@ let SearchScreenInner = ({
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
query,
|
||||
queryWithParams,
|
||||
filters,
|
||||
hasFilters,
|
||||
headerHeight,
|
||||
@@ -737,7 +713,6 @@ let SearchScreenInner = ({
|
||||
activeTab: number
|
||||
setActiveTab: React.Dispatch<React.SetStateAction<number>>
|
||||
query: string
|
||||
queryWithParams: string
|
||||
filters: SearchFilters
|
||||
hasFilters: boolean
|
||||
headerHeight: number
|
||||
@@ -754,7 +729,6 @@ let SearchScreenInner = ({
|
||||
return query || hasFilters ? (
|
||||
<SearchResults
|
||||
query={query}
|
||||
queryWithParams={queryWithParams}
|
||||
filters={filters}
|
||||
hasFilters={hasFilters}
|
||||
activeTab={activeTab}
|
||||
@@ -840,7 +814,6 @@ function useQueryManager({
|
||||
return useMemo(
|
||||
() => ({
|
||||
query,
|
||||
queryWithParams: makeSearchQuery(query, filtersToLegacyParams(filters)),
|
||||
filters,
|
||||
setFilters,
|
||||
hasFilters: hasActiveFilters(filters),
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
countActiveFilters,
|
||||
definedFilterParams,
|
||||
filtersToApiParams,
|
||||
filtersToLegacyParams,
|
||||
hasPostOnlyFilters,
|
||||
parseHistoryEntry,
|
||||
readSearchFilters,
|
||||
@@ -76,34 +75,6 @@ describe(`searchParams`, () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe(`filtersToLegacyParams`, () => {
|
||||
it(`maps structured filters back to legacy query operators`, () => {
|
||||
expect(
|
||||
filtersToLegacyParams({
|
||||
author: 'alice',
|
||||
mentions: 'bob',
|
||||
domain: 'bsky.app',
|
||||
url: 'bsky.app/x',
|
||||
tag: 'atproto',
|
||||
lang: 'en',
|
||||
since: '2024-01-01',
|
||||
until: '2024-02-01',
|
||||
media: 'true',
|
||||
replies: 'none',
|
||||
}),
|
||||
).toEqual({
|
||||
from: 'alice',
|
||||
mentions: 'bob',
|
||||
domain: 'bsky.app',
|
||||
url: 'bsky.app/x',
|
||||
tag: 'atproto',
|
||||
lang: 'en',
|
||||
since: '2024-01-01',
|
||||
until: '2024-02-01',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe(`filtersToApiParams`, () => {
|
||||
it(`splits list fields into arrays and maps v2-only filters`, () => {
|
||||
expect(
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import {useMemo} from 'react'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {languageName} from '#/locale/helpers'
|
||||
import {APP_LANGUAGES, LANGUAGES} from '#/locale/languages'
|
||||
import {useLanguagePrefs} from '#/state/preferences'
|
||||
import {atoms as a, native, platform, tokens} from '#/alf'
|
||||
import {atoms as a, native, platform} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {
|
||||
ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon,
|
||||
@@ -14,7 +12,6 @@ import {
|
||||
} from '#/components/icons/Chevron'
|
||||
import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Globe'
|
||||
import * as Menu from '#/components/Menu'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
|
||||
export function SearchLanguageDropdown({
|
||||
showIcon = true,
|
||||
@@ -25,14 +22,9 @@ export function SearchLanguageDropdown({
|
||||
value: string
|
||||
onChange(value: string): void
|
||||
}) {
|
||||
const ax = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const {appLanguage, contentLanguages, primaryLanguage} = useLanguagePrefs()
|
||||
|
||||
const searchV2Enabled = ax.features.enabled(ax.features.SearchV2Enable)
|
||||
const advancedSearchV2Enabled =
|
||||
searchV2Enabled && ax.features.enabled(ax.features.AdvancedSearchV2Enable)
|
||||
|
||||
const languages = useMemo(() => {
|
||||
return LANGUAGES.filter(
|
||||
(lang, index, self) =>
|
||||
@@ -70,40 +62,20 @@ export function SearchLanguageDropdown({
|
||||
}, [appLanguage, contentLanguages, primaryLanguage])
|
||||
|
||||
const currentLanguageLabel =
|
||||
languages.find(lang => lang.value === value)?.label ?? _(msg`All languages`)
|
||||
languages.find(lang => lang.value === value)?.label ?? l`All languages`
|
||||
|
||||
return (
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
label={_(
|
||||
msg`Filter search by language (currently: ${currentLanguageLabel})`,
|
||||
)}>
|
||||
label={l`Filter search by language (currently: ${currentLanguageLabel})`}>
|
||||
{({props}) => (
|
||||
<Button
|
||||
{...props}
|
||||
label={props.accessibilityLabel}
|
||||
size="small"
|
||||
color={
|
||||
advancedSearchV2Enabled
|
||||
? 'secondary'
|
||||
: platform({native: 'primary', default: 'secondary'})
|
||||
}
|
||||
variant={
|
||||
advancedSearchV2Enabled
|
||||
? 'solid'
|
||||
: platform({native: 'ghost', default: 'solid'})
|
||||
}
|
||||
style={
|
||||
showIcon
|
||||
? native([
|
||||
a.py_sm,
|
||||
a.px_sm,
|
||||
advancedSearchV2Enabled
|
||||
? null
|
||||
: {marginRight: tokens.space.sm * -1},
|
||||
])
|
||||
: null
|
||||
}>
|
||||
color="secondary"
|
||||
variant="solid"
|
||||
style={showIcon ? native([a.py_sm, a.px_sm]) : null}>
|
||||
{showIcon ? <ButtonIcon icon={EarthIcon} /> : null}
|
||||
<ButtonText>{currentLanguageLabel}</ButtonText>
|
||||
<ButtonIcon
|
||||
@@ -119,7 +91,7 @@ export function SearchLanguageDropdown({
|
||||
<Menu.LabelText>
|
||||
<Trans>Filter search by language</Trans>
|
||||
</Menu.LabelText>
|
||||
<Menu.Item label={_(msg`All languages`)} onPress={() => onChange('')}>
|
||||
<Menu.Item label={l`All languages`} onPress={() => onChange('')}>
|
||||
<Menu.ItemText>
|
||||
<Trans>All languages</Trans>
|
||||
</Menu.ItemText>
|
||||
|
||||
@@ -188,27 +188,6 @@ export function withoutFilterParams(
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts structured filters back into the legacy free-text operators used by
|
||||
* search v1. UI-only filters are intentionally dropped because the old path
|
||||
* could not apply them.
|
||||
*/
|
||||
export function filtersToLegacyParams(
|
||||
filters: SearchFilters,
|
||||
): Record<string, string> {
|
||||
const params: Record<string, string> = {}
|
||||
if (filters.author) params.from = filters.author
|
||||
if (filters.mentions) params.mentions = filters.mentions
|
||||
if (filters.domain) params.domain = filters.domain
|
||||
if (filters.url) params.url = filters.url
|
||||
if (filters.tag) params.tag = filters.tag
|
||||
if (filters.lang) params.lang = filters.lang
|
||||
if (filters.since) params.since = filters.since
|
||||
if (filters.until) params.until = filters.until
|
||||
return params
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps each multi-value SearchFilters key to its `app.bsky.feed.searchPostsV2`
|
||||
* param name. Search v1 only honored the first value for the singular lexicon
|
||||
|
||||
@@ -30,15 +30,3 @@ export function parseSearchQuery(rawQuery: string) {
|
||||
params,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeSearchQuery(query: string, params: Params) {
|
||||
return [
|
||||
query,
|
||||
Object.entries(params)
|
||||
.filter(([_, value]) => value)
|
||||
.map(([name, value]) => `${name}:${value}`)
|
||||
.join(' '),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
+13
-14
@@ -1,8 +1,7 @@
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {type ListRenderItemInfo, View} from 'react-native'
|
||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
|
||||
import {HITSLOP_10} from '#/lib/constants'
|
||||
@@ -12,7 +11,7 @@ import {type CommonNavigatorParams} from '#/lib/routes/types'
|
||||
import {shareUrl} from '#/lib/sharing'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {enforceLen} from '#/lib/strings/helpers'
|
||||
import {useSearchPostsQuery} from '#/state/queries/search-posts'
|
||||
import {useSearchPostsV2Query} from '#/state/queries/search-posts-v2'
|
||||
import {Pager} from '#/view/com/pager/Pager'
|
||||
import {TabBar} from '#/view/com/pager/TabBar'
|
||||
import {Post} from '#/view/com/post/Post'
|
||||
@@ -35,7 +34,7 @@ export default function TopicScreen({
|
||||
route,
|
||||
}: NativeStackScreenProps<CommonNavigatorParams, 'Topic'>) {
|
||||
const {topic} = route.params
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
|
||||
const headerTitle = useMemo(() => {
|
||||
return enforceLen(decodeURIComponent(topic), 24, true, 'middle')
|
||||
@@ -44,7 +43,7 @@ export default function TopicScreen({
|
||||
const onShare = useCallback(() => {
|
||||
const url = new URL('https://bsky.app')
|
||||
url.pathname = `/topic/${topic}`
|
||||
shareUrl(url.toString())
|
||||
void shareUrl(url.toString())
|
||||
}, [topic])
|
||||
|
||||
const [activeTab, setActiveTab] = useState(0)
|
||||
@@ -56,13 +55,13 @@ export default function TopicScreen({
|
||||
const sections = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
title: _(msg`Top`),
|
||||
title: l`Top`,
|
||||
component: (
|
||||
<TopicScreenTab topic={topic} sort="top" active={activeTab === 0} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: _(msg`Latest`),
|
||||
title: l`Latest`,
|
||||
component: (
|
||||
<TopicScreenTab
|
||||
topic={topic}
|
||||
@@ -72,7 +71,7 @@ export default function TopicScreen({
|
||||
),
|
||||
},
|
||||
]
|
||||
}, [_, topic, activeTab])
|
||||
}, [l, topic, activeTab])
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
@@ -87,7 +86,7 @@ export default function TopicScreen({
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot>
|
||||
<Button
|
||||
label={_(msg`Share`)}
|
||||
label={l`Share`}
|
||||
size="small"
|
||||
variant="ghost"
|
||||
color="primary"
|
||||
@@ -120,7 +119,7 @@ function TopicScreenTab({
|
||||
sort: 'top' | 'latest'
|
||||
active: boolean
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const initialNumToRender = useInitialNumToRender()
|
||||
const [isPTR, setIsPTR] = useState(false)
|
||||
const trackPostView = usePostViewTracking('Topic')
|
||||
@@ -135,7 +134,7 @@ function TopicScreenTab({
|
||||
refetch,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
} = useSearchPostsQuery({
|
||||
} = useSearchPostsV2Query({
|
||||
query: decodeURIComponent(topic),
|
||||
sort,
|
||||
enabled: active,
|
||||
@@ -153,7 +152,7 @@ function TopicScreenTab({
|
||||
|
||||
const onEndReached = useCallback(() => {
|
||||
if (isFetchingNextPage || !hasNextPage || error) return
|
||||
fetchNextPage()
|
||||
void fetchNextPage()
|
||||
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
|
||||
|
||||
return (
|
||||
@@ -164,7 +163,7 @@ function TopicScreenTab({
|
||||
isError={isError}
|
||||
onRetry={refetch}
|
||||
emptyType="results"
|
||||
emptyMessage={_(msg`We couldn't find any results for that topic.`)}
|
||||
emptyMessage={l`We couldn't find any results for that topic.`}
|
||||
/>
|
||||
) : (
|
||||
<List
|
||||
@@ -172,7 +171,7 @@ function TopicScreenTab({
|
||||
renderItem={renderItem}
|
||||
keyExtractor={keyExtractor}
|
||||
refreshing={isPTR}
|
||||
onRefresh={onRefresh}
|
||||
onRefresh={() => void onRefresh()}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={4}
|
||||
onItemSeen={trackPostView}
|
||||
|
||||
Reference in New Issue
Block a user