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 {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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user