Update advanced search UI (#11088)

This commit is contained in:
DS Boyce
2026-07-08 10:14:39 -07:00
committed by GitHub
parent decac43c34
commit 510dc51d7a
8 changed files with 75 additions and 75 deletions
-3
View File
@@ -671,9 +671,6 @@
}
},
"src/components/forms/TextField.tsx": {
"@typescript-eslint/no-unsafe-member-access": {
"count": 3
},
"react-hooks/refs": {
"count": 1
}
+1 -1
View File
@@ -207,7 +207,7 @@ export function createInput(Component: typeof TextInput) {
const refs = mergeRefs([ctx.inputRef, inputRef!].filter(Boolean))
const flattened = StyleSheet.flatten([
const flattened = StyleSheet.flatten<TextStyle>([
a.relative,
a.z_20,
a.flex_1,
+40 -22
View File
@@ -540,6 +540,24 @@ export function SearchScreenShell({
value={filters.lang ?? ''}
onChange={onChangeLang}
/>
) : showFilters && advancedSearchV2Enabled ? (
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
<AdvancedSearchDialog
disabled={activeTab > 1}
q={searchText}
filters={filters}
onSubmit={onSubmitAdvanced}
/>
<Button
accessibilityRole="button"
size="small"
color="secondary"
shape="round"
label={l`Share this search`}
onPress={onShareSearch}>
<ButtonIcon icon={ShareIcon} />
</Button>
</View>
) : (
<Layout.Header.Slot />
)}
@@ -558,19 +576,19 @@ export function SearchScreenShell({
/>
)}
<View style={[a.w_full, a.flex_row, a.align_stretch, a.gap_xs]}>
<View style={[a.w_full, a.flex_row, a.align_stretch, a.gap_sm]}>
<View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
{showAutocomplete && (
<Button
label={l`Cancel search`}
size="large"
size="small"
variant="ghost"
color="secondary"
shape="round"
style={[a.px_sm]}
onPress={onPressCancelSearch}
hitSlop={HITSLOP_10}>
<ButtonIcon icon={ArrowLeftIcon} />
<ButtonIcon icon={ArrowLeftIcon} size="lg" />
</Button>
)}
<View style={[a.flex_1]}>
@@ -591,6 +609,25 @@ export function SearchScreenShell({
/>
</View>
</View>
{showFilters && !showHeader && advancedSearchV2Enabled ? (
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
<AdvancedSearchDialog
disabled={activeTab > 1}
q={searchText}
filters={filters}
onSubmit={onSubmitAdvanced}
/>
<Button
accessibilityRole="button"
size="small"
color="secondary"
shape="round"
label={l`Share this search`}
onPress={onShareSearch}>
<ButtonIcon icon={ShareIcon} />
</Button>
</View>
) : null}
</View>
{showFilters && !showHeader && !advancedSearchV2Enabled && (
@@ -601,25 +638,6 @@ export function SearchScreenShell({
/>
</View>
)}
{showFilters && advancedSearchV2Enabled ? (
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
<AdvancedSearchDialog
q={searchText}
filters={filters}
onSubmit={onSubmitAdvanced}
/>
<Button
accessibilityRole="button"
size="small"
color="secondary"
shape="round"
label={l`Share this search`}
onPress={onShareSearch}>
<ButtonIcon icon={ShareIcon} />
</Button>
</View>
) : null}
</View>
</View>
</Layout.Center>
@@ -41,7 +41,7 @@ export function ClearableDateField({
*/
value={active ? value : ''}
placeholder={l({
message: 'Any time',
message: 'Any date',
comment: 'Placeholder text for a date picker',
})}
accessibilityHint={accessibilityHint}
@@ -19,11 +19,10 @@ export function FollowingDropdown({
const {t: l} = useLingui()
const options: {value: FollowingFilter; label: string}[] = [
{value: 'everyone', label: l`Everyone`},
{value: 'anyone', label: l`Anyone`},
{value: 'following', label: l`People you follow`},
]
const currentLabel =
options.find(o => o.value === value)?.label ?? l`Everyone`
const currentLabel = options.find(o => o.value === value)?.label ?? l`Anyone`
return (
<Menu.Root>
@@ -14,7 +14,7 @@ const emptySerializeState = {
language: '',
replies: 'all' as const,
media: 'all' as const,
following: 'everyone' as const,
following: 'anyone' as const,
dateSince: '',
dateSinceActive: false,
dateUntil: '',
@@ -145,10 +145,10 @@ describe(`AdvancedSearchDialog serialize/parse`, () => {
expect(out.filters.following).toBe('true')
})
it(`leaves the following param unset for everyone`, () => {
it(`leaves the following param unset for anyone`, () => {
const out = serializeAdvancedSearch({
...emptySerializeState,
following: 'everyone',
following: 'anyone',
})
expect(out.filters.following).toBeUndefined()
})
@@ -157,7 +157,7 @@ describe(`AdvancedSearchDialog serialize/parse`, () => {
expect(parseAdvancedSearch('', {following: 'true'}).following).toBe(
'following',
)
expect(parseAdvancedSearch('', {}).following).toBe('everyone')
expect(parseAdvancedSearch('', {}).following).toBe('anyone')
})
it(`strips redundant markers from filter values on serialize`, () => {
@@ -177,7 +177,7 @@ describe(`AdvancedSearchDialog serialize/parse`, () => {
language: '',
replies: 'all',
media: 'all',
following: 'everyone',
following: 'anyone',
dateSince: '',
dateSinceActive: false,
dateUntil: '',
@@ -38,17 +38,18 @@ import {
const MAX_FILTERS = 20
export function AdvancedSearchDialog({
disabled,
q,
filters,
onSubmit,
}: {
disabled: boolean
q: string
filters: SearchFilters
onSubmit: (q: string, filters: SearchFilters) => void
}) {
const ax = useAnalytics()
const {t: l} = useLingui()
const t = useTheme()
const control = Dialog.useDialogControl()
const filtersActive = hasActiveFilters(filters)
const stateKey = useMemo(
@@ -61,9 +62,10 @@ export function AdvancedSearchDialog({
<>
<View style={[a.relative]}>
<Button
disabled={disabled}
label={l`Open advanced search options`}
size="small"
color="secondary"
color={filtersActive ? 'primary_subtle' : 'secondary'}
style={native([a.py_sm, a.px_sm])}
onPress={() => {
ax.metric('search:advanced:press', {
@@ -73,25 +75,9 @@ export function AdvancedSearchDialog({
}}>
<ButtonIcon icon={SettingsSliderIcon} />
<ButtonText>
<Trans>Advanced search</Trans>
<Trans context="search">Filters</Trans>
</ButtonText>
</Button>
{filtersActive && (
<View
accessible={false}
style={[
a.absolute,
a.rounded_full,
{
top: -2,
right: -2,
width: 10,
height: 10,
backgroundColor: t.palette.primary_500,
},
]}
/>
)}
</View>
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
@@ -244,12 +230,12 @@ function DialogInner({
return (
<Dialog.ScrollableInner
ref={scrollRef}
label={l`Dialog: Set advanced search options`}
label={l`Dialog: Set search filters`}
contentContainerStyle={[a.px_0, a.pt_0]}
header={
<Dialog.Header renderLeft={cancelButton} renderRight={searchButton}>
<Dialog.HeaderText>
<Trans>Advanced search</Trans>
<Trans context="search">Filters</Trans>
</Dialog.HeaderText>
</Dialog.Header>
}>
@@ -272,22 +258,6 @@ function DialogInner({
</View>
<View style={[twoColumn ? a.flex_row : a.flex_col, a.gap_xl]}>
<View style={[a.flex_1]}>
<TextField.LabelText>
<Trans>This exact phrase</Trans>
</TextField.LabelText>
<ClearableInput
label={l`This exact phrase`}
defaultValue={exactPhrase}
placeholder={l({
message: 'whats up',
comment: 'Advanced search: Example of an “exact phrase” search',
})}
onChangeText={setExactPhrase}
onSubmitEditing={handlePressSearch}
/>
</View>
<View style={[a.flex_1]}>
<TextField.LabelText>
<Trans>None of these words</Trans>
@@ -304,6 +274,22 @@ function DialogInner({
onSubmitEditing={handlePressSearch}
/>
</View>
<View style={[a.flex_1]}>
<TextField.LabelText>
<Trans>This exact phrase</Trans>
</TextField.LabelText>
<ClearableInput
label={l`This exact phrase`}
defaultValue={exactPhrase}
placeholder={l({
message: 'whats up',
comment: 'Advanced search: Example of an “exact phrase” search',
})}
onChangeText={setExactPhrase}
onSubmitEditing={handlePressSearch}
/>
</View>
</View>
<View>
@@ -15,9 +15,9 @@ export type MediaFilter = 'all' | 'media' | 'video'
/**
* Whether to limit results to authors the user follows. Serializes into the
* `following` sibling param ('following' -> following:true, everyone -> unset).
* `following` sibling param ('following' -> following:true, anyone -> unset).
*/
export type FollowingFilter = 'everyone' | 'following'
export type FollowingFilter = 'anyone' | 'following'
export type FilterField = 'authors' | 'mentions' | 'domains' | 'urls' | 'tags'
@@ -260,7 +260,7 @@ export function parseAdvancedSearch(
language: lang,
replies,
media,
following: filters.following === 'true' ? 'following' : 'everyone',
following: filters.following === 'true' ? 'following' : 'anyone',
since: since && isValidDate(since) ? since : '',
until: until && isValidDate(until) ? until : '',
filters: filterRows,