Update advanced search UI (#11088)
This commit is contained in:
@@ -671,9 +671,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/forms/TextField.tsx": {
|
"src/components/forms/TextField.tsx": {
|
||||||
"@typescript-eslint/no-unsafe-member-access": {
|
|
||||||
"count": 3
|
|
||||||
},
|
|
||||||
"react-hooks/refs": {
|
"react-hooks/refs": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ export function createInput(Component: typeof TextInput) {
|
|||||||
|
|
||||||
const refs = mergeRefs([ctx.inputRef, inputRef!].filter(Boolean))
|
const refs = mergeRefs([ctx.inputRef, inputRef!].filter(Boolean))
|
||||||
|
|
||||||
const flattened = StyleSheet.flatten([
|
const flattened = StyleSheet.flatten<TextStyle>([
|
||||||
a.relative,
|
a.relative,
|
||||||
a.z_20,
|
a.z_20,
|
||||||
a.flex_1,
|
a.flex_1,
|
||||||
|
|||||||
@@ -540,6 +540,24 @@ export function SearchScreenShell({
|
|||||||
value={filters.lang ?? ''}
|
value={filters.lang ?? ''}
|
||||||
onChange={onChangeLang}
|
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 />
|
<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]}>
|
<View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
|
||||||
{showAutocomplete && (
|
{showAutocomplete && (
|
||||||
<Button
|
<Button
|
||||||
label={l`Cancel search`}
|
label={l`Cancel search`}
|
||||||
size="large"
|
size="small"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
shape="round"
|
shape="round"
|
||||||
style={[a.px_sm]}
|
style={[a.px_sm]}
|
||||||
onPress={onPressCancelSearch}
|
onPress={onPressCancelSearch}
|
||||||
hitSlop={HITSLOP_10}>
|
hitSlop={HITSLOP_10}>
|
||||||
<ButtonIcon icon={ArrowLeftIcon} />
|
<ButtonIcon icon={ArrowLeftIcon} size="lg" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<View style={[a.flex_1]}>
|
<View style={[a.flex_1]}>
|
||||||
@@ -591,6 +609,25 @@ export function SearchScreenShell({
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</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>
|
</View>
|
||||||
|
|
||||||
{showFilters && !showHeader && !advancedSearchV2Enabled && (
|
{showFilters && !showHeader && !advancedSearchV2Enabled && (
|
||||||
@@ -601,25 +638,6 @@ export function SearchScreenShell({
|
|||||||
/>
|
/>
|
||||||
</View>
|
</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>
|
||||||
</View>
|
</View>
|
||||||
</Layout.Center>
|
</Layout.Center>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export function ClearableDateField({
|
|||||||
*/
|
*/
|
||||||
value={active ? value : ''}
|
value={active ? value : ''}
|
||||||
placeholder={l({
|
placeholder={l({
|
||||||
message: 'Any time',
|
message: 'Any date',
|
||||||
comment: 'Placeholder text for a date picker',
|
comment: 'Placeholder text for a date picker',
|
||||||
})}
|
})}
|
||||||
accessibilityHint={accessibilityHint}
|
accessibilityHint={accessibilityHint}
|
||||||
|
|||||||
@@ -19,11 +19,10 @@ export function FollowingDropdown({
|
|||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const options: {value: FollowingFilter; label: string}[] = [
|
const options: {value: FollowingFilter; label: string}[] = [
|
||||||
{value: 'everyone', label: l`Everyone`},
|
{value: 'anyone', label: l`Anyone`},
|
||||||
{value: 'following', label: l`People you follow`},
|
{value: 'following', label: l`People you follow`},
|
||||||
]
|
]
|
||||||
const currentLabel =
|
const currentLabel = options.find(o => o.value === value)?.label ?? l`Anyone`
|
||||||
options.find(o => o.value === value)?.label ?? l`Everyone`
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu.Root>
|
<Menu.Root>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const emptySerializeState = {
|
|||||||
language: '',
|
language: '',
|
||||||
replies: 'all' as const,
|
replies: 'all' as const,
|
||||||
media: 'all' as const,
|
media: 'all' as const,
|
||||||
following: 'everyone' as const,
|
following: 'anyone' as const,
|
||||||
dateSince: '',
|
dateSince: '',
|
||||||
dateSinceActive: false,
|
dateSinceActive: false,
|
||||||
dateUntil: '',
|
dateUntil: '',
|
||||||
@@ -145,10 +145,10 @@ describe(`AdvancedSearchDialog serialize/parse`, () => {
|
|||||||
expect(out.filters.following).toBe('true')
|
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({
|
const out = serializeAdvancedSearch({
|
||||||
...emptySerializeState,
|
...emptySerializeState,
|
||||||
following: 'everyone',
|
following: 'anyone',
|
||||||
})
|
})
|
||||||
expect(out.filters.following).toBeUndefined()
|
expect(out.filters.following).toBeUndefined()
|
||||||
})
|
})
|
||||||
@@ -157,7 +157,7 @@ describe(`AdvancedSearchDialog serialize/parse`, () => {
|
|||||||
expect(parseAdvancedSearch('', {following: 'true'}).following).toBe(
|
expect(parseAdvancedSearch('', {following: 'true'}).following).toBe(
|
||||||
'following',
|
'following',
|
||||||
)
|
)
|
||||||
expect(parseAdvancedSearch('', {}).following).toBe('everyone')
|
expect(parseAdvancedSearch('', {}).following).toBe('anyone')
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`strips redundant markers from filter values on serialize`, () => {
|
it(`strips redundant markers from filter values on serialize`, () => {
|
||||||
@@ -177,7 +177,7 @@ describe(`AdvancedSearchDialog serialize/parse`, () => {
|
|||||||
language: '',
|
language: '',
|
||||||
replies: 'all',
|
replies: 'all',
|
||||||
media: 'all',
|
media: 'all',
|
||||||
following: 'everyone',
|
following: 'anyone',
|
||||||
dateSince: '',
|
dateSince: '',
|
||||||
dateSinceActive: false,
|
dateSinceActive: false,
|
||||||
dateUntil: '',
|
dateUntil: '',
|
||||||
|
|||||||
@@ -38,17 +38,18 @@ import {
|
|||||||
const MAX_FILTERS = 20
|
const MAX_FILTERS = 20
|
||||||
|
|
||||||
export function AdvancedSearchDialog({
|
export function AdvancedSearchDialog({
|
||||||
|
disabled,
|
||||||
q,
|
q,
|
||||||
filters,
|
filters,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}: {
|
}: {
|
||||||
|
disabled: boolean
|
||||||
q: string
|
q: string
|
||||||
filters: SearchFilters
|
filters: SearchFilters
|
||||||
onSubmit: (q: string, filters: SearchFilters) => void
|
onSubmit: (q: string, filters: SearchFilters) => void
|
||||||
}) {
|
}) {
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
const filtersActive = hasActiveFilters(filters)
|
const filtersActive = hasActiveFilters(filters)
|
||||||
const stateKey = useMemo(
|
const stateKey = useMemo(
|
||||||
@@ -61,9 +62,10 @@ export function AdvancedSearchDialog({
|
|||||||
<>
|
<>
|
||||||
<View style={[a.relative]}>
|
<View style={[a.relative]}>
|
||||||
<Button
|
<Button
|
||||||
|
disabled={disabled}
|
||||||
label={l`Open advanced search options`}
|
label={l`Open advanced search options`}
|
||||||
size="small"
|
size="small"
|
||||||
color="secondary"
|
color={filtersActive ? 'primary_subtle' : 'secondary'}
|
||||||
style={native([a.py_sm, a.px_sm])}
|
style={native([a.py_sm, a.px_sm])}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
ax.metric('search:advanced:press', {
|
ax.metric('search:advanced:press', {
|
||||||
@@ -73,25 +75,9 @@ export function AdvancedSearchDialog({
|
|||||||
}}>
|
}}>
|
||||||
<ButtonIcon icon={SettingsSliderIcon} />
|
<ButtonIcon icon={SettingsSliderIcon} />
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Advanced search</Trans>
|
<Trans context="search">Filters</Trans>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</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>
|
</View>
|
||||||
|
|
||||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||||
@@ -244,12 +230,12 @@ function DialogInner({
|
|||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
ref={scrollRef}
|
ref={scrollRef}
|
||||||
label={l`Dialog: Set advanced search options`}
|
label={l`Dialog: Set search filters`}
|
||||||
contentContainerStyle={[a.px_0, a.pt_0]}
|
contentContainerStyle={[a.px_0, a.pt_0]}
|
||||||
header={
|
header={
|
||||||
<Dialog.Header renderLeft={cancelButton} renderRight={searchButton}>
|
<Dialog.Header renderLeft={cancelButton} renderRight={searchButton}>
|
||||||
<Dialog.HeaderText>
|
<Dialog.HeaderText>
|
||||||
<Trans>Advanced search</Trans>
|
<Trans context="search">Filters</Trans>
|
||||||
</Dialog.HeaderText>
|
</Dialog.HeaderText>
|
||||||
</Dialog.Header>
|
</Dialog.Header>
|
||||||
}>
|
}>
|
||||||
@@ -272,22 +258,6 @@ function DialogInner({
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={[twoColumn ? a.flex_row : a.flex_col, a.gap_xl]}>
|
<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]}>
|
<View style={[a.flex_1]}>
|
||||||
<TextField.LabelText>
|
<TextField.LabelText>
|
||||||
<Trans>None of these words</Trans>
|
<Trans>None of these words</Trans>
|
||||||
@@ -304,6 +274,22 @@ function DialogInner({
|
|||||||
onSubmitEditing={handlePressSearch}
|
onSubmitEditing={handlePressSearch}
|
||||||
/>
|
/>
|
||||||
</View>
|
</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>
|
||||||
|
|
||||||
<View>
|
<View>
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ export type MediaFilter = 'all' | 'media' | 'video'
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to limit results to authors the user follows. Serializes into the
|
* 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'
|
export type FilterField = 'authors' | 'mentions' | 'domains' | 'urls' | 'tags'
|
||||||
|
|
||||||
@@ -260,7 +260,7 @@ export function parseAdvancedSearch(
|
|||||||
language: lang,
|
language: lang,
|
||||||
replies,
|
replies,
|
||||||
media,
|
media,
|
||||||
following: filters.following === 'true' ? 'following' : 'everyone',
|
following: filters.following === 'true' ? 'following' : 'anyone',
|
||||||
since: since && isValidDate(since) ? since : '',
|
since: since && isValidDate(since) ? since : '',
|
||||||
until: until && isValidDate(until) ? until : '',
|
until: until && isValidDate(until) ? until : '',
|
||||||
filters: filterRows,
|
filters: filterRows,
|
||||||
|
|||||||
Reference in New Issue
Block a user