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