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:
vineyardbovines
2026-04-14 15:27:34 -04:00
parent 054a6878c5
commit 924f3e3cbc
2 changed files with 42 additions and 76 deletions
+1 -13
View File
@@ -10,7 +10,6 @@ import {GifPickerErrorBoundary} from '#/features/gifPicker/components/GifPickerE
import {GifPickerGrid} from '#/features/gifPicker/components/GifPickerGrid' import {GifPickerGrid} from '#/features/gifPicker/components/GifPickerGrid'
import {GifPickerHeader} from '#/features/gifPicker/components/GifPickerHeader' import {GifPickerHeader} from '#/features/gifPicker/components/GifPickerHeader'
import {GifPickerPlaceholder} from '#/features/gifPicker/components/GifPickerPlaceholder' import {GifPickerPlaceholder} from '#/features/gifPicker/components/GifPickerPlaceholder'
import {useGifAutocomplete} from '#/features/gifPicker/hooks/useGifAutocomplete'
import {useGifPickerData} from '#/features/gifPicker/hooks/useGifPickerData' import {useGifPickerData} from '#/features/gifPicker/hooks/useGifPickerData'
import {type Gif} from '#/features/gifPicker/types' import {type Gif} from '#/features/gifPicker/types'
@@ -65,16 +64,7 @@ function GifPickerBody({
const textInputRef = useRef<TextInput>(null) const textInputRef = useRef<TextInput>(null)
const listRef = useRef<ListMethods>(null) const listRef = useRef<ListMethods>(null)
const [rawSearch, setRawSearch] = useState('') const [rawSearch, setRawSearch] = useState('')
const search = useThrottledValue(rawSearch, 500) const search = useThrottledValue(rawSearch, 750)
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 { const {
data, data,
@@ -107,7 +97,6 @@ function GifPickerBody({
const onChangeSearch = (text: string) => { const onChangeSearch = (text: string) => {
setRawSearch(text) setRawSearch(text)
autocomplete.handleTextChange(text)
listRef.current?.scrollToOffset({offset: 0, animated: false}) listRef.current?.scrollToOffset({offset: 0, animated: false})
} }
@@ -118,7 +107,6 @@ function GifPickerBody({
onChangeText={onChangeSearch} onChangeText={onChangeSearch}
onClose={() => control.close()} onClose={() => control.close()}
onEscape={() => control.close()} onEscape={() => control.close()}
autocomplete={autocomplete}
/> />
{!hasData && ( {!hasData && (
<GifPickerPlaceholder <GifPickerPlaceholder
@@ -9,33 +9,34 @@ import * as TextField from '#/components/forms/TextField'
import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow' import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow'
import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass' import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass'
import {IS_WEB} from '#/env' 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({ export function GifPickerHeader({
inputRef, inputRef,
onChangeText, onChangeText,
onClose, onClose,
onEscape, onEscape,
autocomplete,
}: { }: {
inputRef: Ref<TextInput> inputRef: Ref<TextInput>
onChangeText: (text: string) => void onChangeText: (text: string) => void
onClose: () => void onClose: () => void
onEscape: () => void onEscape: () => void
autocomplete: GifAutocompleteState
}) { }) {
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
return ( return (
<View style={[native(a.pt_4xl), a.relative, a.mb_lg, a.pb_sm, t.atoms.bg]}> <View
<View style={[a.flex_row, a.align_center, !gtMobile && web(a.gap_md)]}> 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 && ( {!gtMobile && IS_WEB && (
<Button <Button
size="small" size="small"
@@ -60,36 +61,13 @@ export function GifPickerHeader({
maxLength={50} maxLength={50}
onKeyPress={({nativeEvent}) => { onKeyPress={({nativeEvent}) => {
if (nativeEvent.key === 'Escape') { if (nativeEvent.key === 'Escape') {
if (!autocomplete.handleKeyDown('Escape')) {
onEscape() 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> </TextField.Root>
</View>
{autocomplete.isVisible && ( {/* future: tabs (Trending / Recents / Categories) render here */}
<GifAutocompleteSuggestions
suggestions={autocomplete.suggestions}
activeIndex={autocomplete.activeIndex}
onSelect={autocomplete.selectSuggestion}
/>
)}
</View> </View>
) )
} }