Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'mary-ext-feat/better-autocomplete-view' into main

+114 -35
+36 -14
src/view/screens/Search/Search.tsx
··· 42 42 import {useAnalytics} from '#/lib/analytics/analytics' 43 43 import {MagnifyingGlassIcon} from '#/lib/icons' 44 44 import {useModerationOpts} from '#/state/queries/preferences' 45 - import {SearchResultCard} from '#/view/shell/desktop/Search' 45 + import { 46 + MATCH_HANDLE, 47 + SearchLinkCard, 48 + SearchProfileCard, 49 + } from '#/view/shell/desktop/Search' 46 50 import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell' 47 - import {isWeb} from '#/platform/detection' 51 + import {isNative, isWeb} from '#/platform/detection' 48 52 import {listenSoftReset} from '#/state/events' 49 53 import {s} from '#/lib/styles' 50 54 ··· 509 513 onPressCancelSearch() 510 514 }, [onPressCancelSearch]) 511 515 516 + const queryMaybeHandle = React.useMemo(() => { 517 + const match = MATCH_HANDLE.exec(query) 518 + return match && match[1] 519 + }, [query]) 520 + 512 521 useFocusEffect( 513 522 React.useCallback(() => { 514 523 setMinimalShellMode(false) ··· 615 624 dataSet={{stableGutters: '1'}} 616 625 keyboardShouldPersistTaps="handled" 617 626 keyboardDismissMode="on-drag"> 618 - {searchResults.length ? ( 619 - searchResults.map((item, i) => ( 620 - <SearchResultCard 621 - key={item.did} 622 - profile={item} 623 - moderation={moderateProfile(item, moderationOpts)} 624 - style={i === 0 ? {borderTopWidth: 0} : {}} 625 - /> 626 - )) 627 - ) : ( 628 - <EmptyState message={_(msg`No results found for ${query}`)} /> 629 - )} 627 + <SearchLinkCard 628 + label={_(msg`Search for "${query}"`)} 629 + onPress={isNative ? onSubmit : undefined} 630 + to={ 631 + isNative 632 + ? undefined 633 + : `/search?q=${encodeURIComponent(query)}` 634 + } 635 + style={{borderBottomWidth: 1}} 636 + /> 637 + 638 + {queryMaybeHandle ? ( 639 + <SearchLinkCard 640 + label={_(msg`Go to @${queryMaybeHandle}`)} 641 + to={`/profile/${queryMaybeHandle}`} 642 + /> 643 + ) : null} 644 + 645 + {searchResults.map(item => ( 646 + <SearchProfileCard 647 + key={item.did} 648 + profile={item} 649 + moderation={moderateProfile(item, moderationOpts)} 650 + /> 651 + ))} 630 652 631 653 <View style={{height: 200}} /> 632 654 </ScrollView>
+78 -21
src/view/shell/desktop/Search.tsx
··· 29 29 import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete' 30 30 import {useModerationOpts} from '#/state/queries/preferences' 31 31 32 - export function SearchResultCard({ 32 + export const MATCH_HANDLE = 33 + /@?([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,}))/ 34 + 35 + export function SearchLinkCard({ 36 + label, 37 + to, 38 + onPress, 39 + style, 40 + }: { 41 + label: string 42 + to?: string 43 + onPress?: () => void 44 + style?: ViewStyle 45 + }) { 46 + const pal = usePalette('default') 47 + 48 + const inner = ( 49 + <View 50 + style={[pal.border, {paddingVertical: 16, paddingHorizontal: 12}, style]}> 51 + <Text type="md" style={[pal.text]}> 52 + {label} 53 + </Text> 54 + </View> 55 + ) 56 + 57 + if (onPress) { 58 + return ( 59 + <TouchableOpacity 60 + onPress={onPress} 61 + accessibilityLabel={label} 62 + accessibilityHint=""> 63 + {inner} 64 + </TouchableOpacity> 65 + ) 66 + } 67 + 68 + return ( 69 + <Link href={to} asAnchor anchorNoUnderline> 70 + <View 71 + style={[ 72 + pal.border, 73 + {paddingVertical: 16, paddingHorizontal: 12}, 74 + style, 75 + ]}> 76 + <Text type="md" style={[pal.text]}> 77 + {label} 78 + </Text> 79 + </View> 80 + </Link> 81 + ) 82 + } 83 + 84 + export function SearchProfileCard({ 33 85 profile, 34 - style, 35 86 moderation, 36 87 }: { 37 88 profile: AppBskyActorDefs.ProfileViewBasic 38 - style: ViewStyle 39 89 moderation: ProfileModeration 40 90 }) { 41 91 const pal = usePalette('default') ··· 50 100 <View 51 101 style={[ 52 102 pal.border, 53 - style, 54 103 { 55 - borderTopWidth: 1, 56 104 flexDirection: 'row', 57 105 alignItems: 'center', 58 106 gap: 12, ··· 147 195 navigation.dispatch(StackActions.push('Search', {q: query})) 148 196 }, [query, navigation, setSearchResults]) 149 197 198 + const queryMaybeHandle = React.useMemo(() => { 199 + const match = MATCH_HANDLE.exec(query) 200 + return match && match[1] 201 + }, [query]) 202 + 150 203 return ( 151 204 <View style={[styles.container, pal.view]}> 152 205 <View ··· 198 251 </View> 199 252 ) : ( 200 253 <> 201 - {searchResults.length ? ( 202 - searchResults.map((item, i) => ( 203 - <SearchResultCard 204 - key={item.did} 205 - profile={item} 206 - moderation={moderateProfile(item, moderationOpts)} 207 - style={i === 0 ? {borderTopWidth: 0} : {}} 208 - /> 209 - )) 210 - ) : ( 211 - <View> 212 - <Text style={[pal.textLight, styles.noResults]}> 213 - <Trans>No results found for {query}</Trans> 214 - </Text> 215 - </View> 216 - )} 254 + <SearchLinkCard 255 + label={_(msg`Search for "${query}"`)} 256 + to={`/search?q=${encodeURIComponent(query)}`} 257 + style={{borderBottomWidth: 1}} 258 + /> 259 + 260 + {queryMaybeHandle ? ( 261 + <SearchLinkCard 262 + label={_(msg`Go to @${queryMaybeHandle}`)} 263 + to={`/profile/${queryMaybeHandle}`} 264 + /> 265 + ) : null} 266 + 267 + {searchResults.map(item => ( 268 + <SearchProfileCard 269 + key={item.did} 270 + profile={item} 271 + moderation={moderateProfile(item, moderationOpts)} 272 + /> 273 + ))} 217 274 </> 218 275 )} 219 276 </View>