Bluesky app fork with some witchin' additions 馃挮
at main 167 lines 4.3 kB view raw
1import {memo, useCallback, useState} from 'react' 2import { 3 ActivityIndicator, 4 StyleSheet, 5 TouchableOpacity, 6 View, 7 type ViewStyle, 8} from 'react-native' 9import {useLingui} from '@lingui/react/macro' 10import {StackActions, useNavigation} from '@react-navigation/native' 11 12import {usePalette} from '#/lib/hooks/usePalette' 13import {type NavigationProp} from '#/lib/routes/types' 14import {useModerationOpts} from '#/state/preferences/moderation-opts' 15import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' 16import {Link} from '#/view/com/util/Link' 17import {Text} from '#/view/com/util/text/Text' 18import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard' 19import {atoms as a, useTheme} from '#/alf' 20import {SearchInput} from '#/components/forms/SearchInput' 21 22let SearchLinkCard = ({ 23 label, 24 to, 25 onPress, 26 style, 27}: { 28 label: string 29 to?: string 30 onPress?: () => void 31 style?: ViewStyle 32}): React.ReactNode => { 33 const pal = usePalette('default') 34 35 const inner = ( 36 <View 37 style={[pal.border, {paddingVertical: 16, paddingHorizontal: 12}, style]}> 38 <Text type="md" style={[pal.text]}> 39 {label} 40 </Text> 41 </View> 42 ) 43 44 if (onPress) { 45 return ( 46 <TouchableOpacity 47 onPress={onPress} 48 accessibilityLabel={label} 49 accessibilityHint=""> 50 {inner} 51 </TouchableOpacity> 52 ) 53 } 54 55 return ( 56 <Link href={to} asAnchor anchorNoUnderline> 57 <View 58 style={[ 59 pal.border, 60 {paddingVertical: 16, paddingHorizontal: 12}, 61 style, 62 ]}> 63 <Text type="md" style={[pal.text]}> 64 {label} 65 </Text> 66 </View> 67 </Link> 68 ) 69} 70SearchLinkCard = memo(SearchLinkCard) 71export {SearchLinkCard} 72 73export function DesktopSearch() { 74 const t = useTheme() 75 const {t: l} = useLingui() 76 const pal = usePalette('default') 77 const navigation = useNavigation<NavigationProp>() 78 const [isActive, setIsActive] = useState<boolean>(false) 79 const [query, setQuery] = useState<string>('') 80 const {data: autocompleteData, isFetching} = useActorAutocompleteQuery( 81 query, 82 true, 83 ) 84 85 const moderationOpts = useModerationOpts() 86 87 const onChangeText = useCallback((text: string) => { 88 setQuery(text) 89 setIsActive(text.length > 0) 90 }, []) 91 92 const onPressCancelSearch = useCallback(() => { 93 setQuery('') 94 setIsActive(false) 95 }, [setQuery]) 96 97 const onSubmit = useCallback(() => { 98 setIsActive(false) 99 if (!query.length) return 100 navigation.dispatch(StackActions.push('Search', {q: query})) 101 }, [query, navigation]) 102 103 const onSearchProfileCardPress = useCallback(() => { 104 setQuery('') 105 setIsActive(false) 106 }, []) 107 108 return ( 109 <View style={[styles.container, pal.view]}> 110 <SearchInput 111 value={query} 112 onChangeText={onChangeText} 113 onClearText={onPressCancelSearch} 114 onSubmitEditing={onSubmit} 115 /> 116 {query !== '' && isActive && moderationOpts && ( 117 <View 118 style={[ 119 pal.view, 120 pal.borderDark, 121 styles.resultsContainer, 122 a.overflow_hidden, 123 ]}> 124 {isFetching && !autocompleteData?.length ? ( 125 <View style={{padding: 8}}> 126 <ActivityIndicator color={t.palette.primary_500} /> 127 </View> 128 ) : ( 129 <> 130 <SearchLinkCard 131 label={l`Search for "${query}"`} 132 to={`/search?q=${encodeURIComponent(query)}`} 133 style={ 134 (autocompleteData?.length ?? 0) > 0 135 ? {borderBottomWidth: 1} 136 : undefined 137 } 138 /> 139 {autocompleteData?.map(item => ( 140 <SearchProfileCard 141 key={item.did} 142 profile={item} 143 moderationOpts={moderationOpts} 144 onPress={onSearchProfileCardPress} 145 /> 146 ))} 147 </> 148 )} 149 </View> 150 )} 151 </View> 152 ) 153} 154 155const styles = StyleSheet.create({ 156 container: { 157 position: 'relative', 158 width: '100%', 159 }, 160 resultsContainer: { 161 marginTop: 10, 162 flexDirection: 'column', 163 width: '100%', 164 borderWidth: 1, 165 borderRadius: 6, 166 }, 167})