import React from 'react' import { ViewStyle, TextInput, View, StyleSheet, TouchableOpacity, ActivityIndicator, } from 'react-native' import {useNavigation, StackActions} from '@react-navigation/native' import { AppBskyActorDefs, moderateProfile, ModerationDecision, } from '@atproto/api' import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {s} from '#/lib/styles' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {makeProfileLink} from '#/lib/routes/links' import {Link} from '#/view/com/util/Link' import {usePalette} from 'lib/hooks/usePalette' import {MagnifyingGlassIcon2} from 'lib/icons' import {NavigationProp} from 'lib/routes/types' import {Text} from 'view/com/util/text/Text' import {UserAvatar} from '#/view/com/util/UserAvatar' import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete' import {useModerationOpts} from '#/state/queries/preferences' export const MATCH_HANDLE = /@?([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,}))/ export function SearchLinkCard({ label, to, onPress, style, }: { label: string to?: string onPress?: () => void style?: ViewStyle }) { const pal = usePalette('default') const inner = ( {label} ) if (onPress) { return ( {inner} ) } return ( {label} ) } export function SearchProfileCard({ profile, moderation, }: { profile: AppBskyActorDefs.ProfileViewBasic moderation: ModerationDecision }) { const pal = usePalette('default') return ( {sanitizeDisplayName( profile.displayName || sanitizeHandle(profile.handle), moderation.ui('displayName'), )} {sanitizeHandle(profile.handle, '@')} ) } export function DesktopSearch() { const {_} = useLingui() const pal = usePalette('default') const navigation = useNavigation() const searchDebounceTimeout = React.useRef( undefined, ) const [isActive, setIsActive] = React.useState(false) const [isFetching, setIsFetching] = React.useState(false) const [query, setQuery] = React.useState('') const [searchResults, setSearchResults] = React.useState< AppBskyActorDefs.ProfileViewBasic[] >([]) const moderationOpts = useModerationOpts() const search = useActorAutocompleteFn() const onChangeText = React.useCallback( async (text: string) => { setQuery(text) if (text.length > 0) { setIsFetching(true) setIsActive(true) if (searchDebounceTimeout.current) clearTimeout(searchDebounceTimeout.current) searchDebounceTimeout.current = setTimeout(async () => { const results = await search({query: text}) if (results) { setSearchResults(results) setIsFetching(false) } }, 300) } else { if (searchDebounceTimeout.current) clearTimeout(searchDebounceTimeout.current) setSearchResults([]) setIsFetching(false) setIsActive(false) } }, [setQuery, search, setSearchResults], ) const onPressCancelSearch = React.useCallback(() => { setQuery('') setIsActive(false) if (searchDebounceTimeout.current) clearTimeout(searchDebounceTimeout.current) }, [setQuery]) const onSubmit = React.useCallback(() => { setIsActive(false) if (!query.length) return setSearchResults([]) if (searchDebounceTimeout.current) clearTimeout(searchDebounceTimeout.current) navigation.dispatch(StackActions.push('Search', {q: query})) }, [query, navigation, setSearchResults]) const queryMaybeHandle = React.useMemo(() => { const match = MATCH_HANDLE.exec(query) return match && match[1] }, [query]) return ( {query ? ( Cancel ) : undefined} {query !== '' && isActive && moderationOpts && ( {isFetching ? ( ) : ( <> {queryMaybeHandle ? ( ) : null} {searchResults.map(item => ( ))} )} )} ) } const styles = StyleSheet.create({ container: { position: 'relative', width: 300, }, search: { paddingHorizontal: 16, paddingVertical: 2, width: 300, borderRadius: 20, }, inputContainer: { flexDirection: 'row', }, iconWrapper: { position: 'relative', top: 2, paddingVertical: 7, marginRight: 8, }, input: { flex: 1, fontSize: 18, width: '100%', paddingTop: 7, paddingBottom: 7, }, cancelBtn: { paddingRight: 4, paddingLeft: 10, paddingVertical: 7, }, resultsContainer: { marginTop: 10, flexDirection: 'column', width: 300, borderWidth: 1, borderRadius: 6, }, noResults: { textAlign: 'center', paddingVertical: 10, }, })