refactor(gif): remove text autocomplete, increase search debounce
Remove the inline text suggestion list from the GIF picker. The Threads-style approach of just letting the GIF search results update directly is simpler and gets users to the GIFs faster. Increase the search throttle from 500ms to 750ms to reduce churn while typing and give results time to load before the next query fires. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,6 @@ import {GifPickerErrorBoundary} from '#/features/gifPicker/components/GifPickerE
|
||||
import {GifPickerGrid} from '#/features/gifPicker/components/GifPickerGrid'
|
||||
import {GifPickerHeader} from '#/features/gifPicker/components/GifPickerHeader'
|
||||
import {GifPickerPlaceholder} from '#/features/gifPicker/components/GifPickerPlaceholder'
|
||||
import {useGifAutocomplete} from '#/features/gifPicker/hooks/useGifAutocomplete'
|
||||
import {useGifPickerData} from '#/features/gifPicker/hooks/useGifPickerData'
|
||||
import {type Gif} from '#/features/gifPicker/types'
|
||||
|
||||
@@ -65,16 +64,7 @@ function GifPickerBody({
|
||||
const textInputRef = useRef<TextInput>(null)
|
||||
const listRef = useRef<ListMethods>(null)
|
||||
const [rawSearch, setRawSearch] = useState('')
|
||||
const search = useThrottledValue(rawSearch, 500)
|
||||
|
||||
const autocomplete = useGifAutocomplete({
|
||||
onSelectSuggestion: text => {
|
||||
setRawSearch(text)
|
||||
// Set the TextInput's displayed value to match
|
||||
textInputRef.current?.setNativeProps({text})
|
||||
listRef.current?.scrollToOffset({offset: 0, animated: false})
|
||||
},
|
||||
})
|
||||
const search = useThrottledValue(rawSearch, 750)
|
||||
|
||||
const {
|
||||
data,
|
||||
@@ -107,7 +97,6 @@ function GifPickerBody({
|
||||
|
||||
const onChangeSearch = (text: string) => {
|
||||
setRawSearch(text)
|
||||
autocomplete.handleTextChange(text)
|
||||
listRef.current?.scrollToOffset({offset: 0, animated: false})
|
||||
}
|
||||
|
||||
@@ -118,7 +107,6 @@ function GifPickerBody({
|
||||
onChangeText={onChangeSearch}
|
||||
onClose={() => control.close()}
|
||||
onEscape={() => control.close()}
|
||||
autocomplete={autocomplete}
|
||||
/>
|
||||
{!hasData && (
|
||||
<GifPickerPlaceholder
|
||||
|
||||
@@ -9,87 +9,65 @@ import * as TextField from '#/components/forms/TextField'
|
||||
import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow'
|
||||
import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {
|
||||
GIF_AUTOCOMPLETE_LISTBOX_ID,
|
||||
GifAutocompleteSuggestions,
|
||||
suggestionItemId,
|
||||
} from '#/features/gifPicker/components/GifAutocompleteSuggestions'
|
||||
import {type GifAutocompleteState} from '#/features/gifPicker/hooks/useGifAutocomplete'
|
||||
|
||||
export function GifPickerHeader({
|
||||
inputRef,
|
||||
onChangeText,
|
||||
onClose,
|
||||
onEscape,
|
||||
autocomplete,
|
||||
}: {
|
||||
inputRef: Ref<TextInput>
|
||||
onChangeText: (text: string) => void
|
||||
onClose: () => void
|
||||
onEscape: () => void
|
||||
autocomplete: GifAutocompleteState
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
|
||||
return (
|
||||
<View style={[native(a.pt_4xl), a.relative, a.mb_lg, a.pb_sm, t.atoms.bg]}>
|
||||
<View style={[a.flex_row, a.align_center, !gtMobile && web(a.gap_md)]}>
|
||||
{!gtMobile && IS_WEB && (
|
||||
<Button
|
||||
size="small"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
shape="round"
|
||||
onPress={onClose}
|
||||
label={_(msg`Close GIF dialog`)}>
|
||||
<ButtonIcon icon={Arrow} size="md" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<TextField.Root style={[!gtMobile && IS_WEB && a.flex_1]}>
|
||||
<TextField.Icon icon={Search} />
|
||||
<TextField.Input
|
||||
label={_(msg`Search GIFs`)}
|
||||
placeholder={_(msg`Search GIFs`)}
|
||||
onChangeText={onChangeText}
|
||||
returnKeyType="search"
|
||||
clearButtonMode="while-editing"
|
||||
inputRef={inputRef}
|
||||
maxLength={50}
|
||||
onKeyPress={({nativeEvent}) => {
|
||||
if (nativeEvent.key === 'Escape') {
|
||||
if (!autocomplete.handleKeyDown('Escape')) {
|
||||
onEscape()
|
||||
}
|
||||
} else {
|
||||
autocomplete.handleKeyDown(nativeEvent.key)
|
||||
}
|
||||
}}
|
||||
// @ts-ignore web-only ARIA props
|
||||
role={autocomplete.isVisible ? 'combobox' : undefined}
|
||||
aria-controls={
|
||||
autocomplete.isVisible ? GIF_AUTOCOMPLETE_LISTBOX_ID : undefined
|
||||
}
|
||||
aria-expanded={autocomplete.isVisible}
|
||||
aria-autocomplete={autocomplete.isVisible ? 'list' : undefined}
|
||||
aria-activedescendant={
|
||||
autocomplete.isVisible && autocomplete.activeIndex >= 0
|
||||
? suggestionItemId(autocomplete.activeIndex)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</TextField.Root>
|
||||
</View>
|
||||
|
||||
{autocomplete.isVisible && (
|
||||
<GifAutocompleteSuggestions
|
||||
suggestions={autocomplete.suggestions}
|
||||
activeIndex={autocomplete.activeIndex}
|
||||
onSelect={autocomplete.selectSuggestion}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
native(a.pt_4xl),
|
||||
a.relative,
|
||||
a.mb_lg,
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
!gtMobile && web(a.gap_md),
|
||||
a.pb_sm,
|
||||
t.atoms.bg,
|
||||
]}>
|
||||
{!gtMobile && IS_WEB && (
|
||||
<Button
|
||||
size="small"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
shape="round"
|
||||
onPress={onClose}
|
||||
label={_(msg`Close GIF dialog`)}>
|
||||
<ButtonIcon icon={Arrow} size="md" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<TextField.Root style={[!gtMobile && IS_WEB && a.flex_1]}>
|
||||
<TextField.Icon icon={Search} />
|
||||
<TextField.Input
|
||||
label={_(msg`Search GIFs`)}
|
||||
placeholder={_(msg`Search GIFs`)}
|
||||
onChangeText={onChangeText}
|
||||
returnKeyType="search"
|
||||
clearButtonMode="while-editing"
|
||||
inputRef={inputRef}
|
||||
maxLength={50}
|
||||
onKeyPress={({nativeEvent}) => {
|
||||
if (nativeEvent.key === 'Escape') {
|
||||
onEscape()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TextField.Root>
|
||||
|
||||
{/* future: tabs (Trending / Recents / Categories) render here */}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user