import {useState} from 'react' import {Pressable, View} from 'react-native' import {useLingui} from '@lingui/react/macro' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {atoms as a, useTheme} from '#/alf' import {type AutocompleteItem, useAutocomplete} from '#/components/Autocomplete' import {Button, ButtonIcon} from '#/components/Button' import * as Dialog from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' import * as ProfileCard from '#/components/ProfileCard' import { appendSelection, type AutocompleteInputProps, lastTokenOf, } from './shared' /** * Native: Sift's popover positioning doesn't anchor, so the result list is * drawn as an absolutely-positioned overlay directly above the input. Anchoring * above (rather than below) keeps it clear of the on-screen keyboard, and * overlaying (rather than rendering inline) means it doesn't push the focused * input down under the keyboard. See index.tsx for the web (floating) variant. * The typeahead matches the last space-delimited token; selecting a result * completes that token and leaves a trailing space so the next value can be * typed. */ export function AutocompleteInput({ label, value, placeholder, onChangeText, onSubmitEditing, }: AutocompleteInputProps) { const t = useTheme() const {t: l} = useLingui() const [focused, setFocused] = useState(false) const lastToken = lastTokenOf(value) const {items} = useAutocomplete({type: 'profile', query: lastToken}) const showDropdown = focused && lastToken.length > 0 && items.length > 0 function selectItem(item: AutocompleteItem) { const next = appendSelection(value, lastToken, item) if (next !== null) onChangeText(next) } return ( setFocused(true)} onBlur={() => setFocused(false)} onSubmitEditing={onSubmitEditing} /> {value.length > 0 && ( )} {showDropdown && } ) } function OverlayList({ items, onSelect, }: { items: AutocompleteItem[] onSelect: (item: AutocompleteItem) => void }) { const t = useTheme() const moderationOpts = useModerationOpts() if (!moderationOpts) return null /* * The list sits above the input, so reverse it to put the most relevant * result (first) at the bottom, nearest the text the user is typing. */ const ordered = [...items].reverse() return ( {ordered.map((item, index) => { if (item.type !== 'profile') return null return ( onSelect(item)} style={({pressed}) => [ a.py_sm, a.px_md, index !== 0 && a.border_t, t.atoms.border_contrast_low, pressed && t.atoms.bg_contrast_25, ]}> ) })} ) }