Update advanced search UI (#11088)
This commit is contained in:
@@ -671,9 +671,6 @@
|
||||
}
|
||||
},
|
||||
"src/components/forms/TextField.tsx": {
|
||||
"@typescript-eslint/no-unsafe-member-access": {
|
||||
"count": 3
|
||||
},
|
||||
"react-hooks/refs": {
|
||||
"count": 1
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: 'what’s 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: 'what’s 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,
|
||||
|
||||
Reference in New Issue
Block a user