Bluesky app fork with some witchin' additions 💫

ALF search cards (#8210)

authored by

Eric Bailey and committed by
GitHub
faab49bd fb8ab9f2

+78 -85
+5 -4
src/screens/Search/components/AutocompleteResults.tsx
··· 1 1 import {memo} from 'react' 2 2 import {ActivityIndicator, View} from 'react-native' 3 - import {type AppBskyActorDefs, moderateProfile} from '@atproto/api' 3 + import {type AppBskyActorDefs} from '@atproto/api' 4 4 import {msg} from '@lingui/macro' 5 5 import {useLingui} from '@lingui/react' 6 6 7 7 import {isNative} from '#/platform/detection' 8 8 import {useModerationOpts} from '#/state/preferences/moderation-opts' 9 - import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search' 9 + import {SearchLinkCard} from '#/view/shell/desktop/Search' 10 + import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard' 10 11 import {atoms as a, native} from '#/alf' 11 12 import * as Layout from '#/components/Layout' 12 13 ··· 25 26 onResultPress: () => void 26 27 onProfileClick: (profile: AppBskyActorDefs.ProfileViewBasic) => void 27 28 }): React.ReactNode => { 28 - const moderationOpts = useModerationOpts() 29 29 const {_} = useLingui() 30 + const moderationOpts = useModerationOpts() 30 31 return ( 31 32 <> 32 33 {(isAutocompleteFetching && !autocompleteData?.length) || ··· 54 55 <SearchProfileCard 55 56 key={item.did} 56 57 profile={item} 57 - moderation={moderateProfile(item, moderationOpts)} 58 + moderationOpts={moderationOpts} 58 59 onPress={() => { 59 60 onProfileClick(item) 60 61 onResultPress()
+62
src/screens/Search/components/SearchProfileCard.tsx
··· 1 + import {useCallback} from 'react' 2 + import {View} from 'react-native' 3 + import {type AppBskyActorDefs, type ModerationOpts} from '@atproto/api' 4 + import {msg} from '@lingui/macro' 5 + import {useLingui} from '@lingui/react' 6 + import {useQueryClient} from '@tanstack/react-query' 7 + 8 + import {makeProfileLink} from '#/lib/routes/links' 9 + import {unstableCacheProfileView} from '#/state/queries/unstable-profile-cache' 10 + import {atoms as a, useTheme} from '#/alf' 11 + import {Link} from '#/components/Link' 12 + import * as ProfileCard from '#/components/ProfileCard' 13 + 14 + export function SearchProfileCard({ 15 + profile, 16 + moderationOpts, 17 + onPress: onPressInner, 18 + }: { 19 + profile: AppBskyActorDefs.ProfileViewBasic 20 + moderationOpts: ModerationOpts 21 + onPress?: () => void 22 + }) { 23 + const t = useTheme() 24 + const {_} = useLingui() 25 + const qc = useQueryClient() 26 + 27 + const onPress = useCallback(() => { 28 + unstableCacheProfileView(qc, profile) 29 + onPressInner?.() 30 + }, [qc, profile, onPressInner]) 31 + 32 + return ( 33 + <Link 34 + testID={`searchAutoCompleteResult-${profile.handle}`} 35 + to={makeProfileLink(profile)} 36 + label={_(msg`View ${profile.handle}'s profile`)} 37 + onPress={onPress}> 38 + {({hovered, pressed}) => ( 39 + <View 40 + style={[ 41 + a.flex_1, 42 + a.px_md, 43 + a.py_sm, 44 + (hovered || pressed) && t.atoms.bg_contrast_25, 45 + ]}> 46 + <ProfileCard.Outer> 47 + <ProfileCard.Header> 48 + <ProfileCard.Avatar 49 + profile={profile} 50 + moderationOpts={moderationOpts} 51 + /> 52 + <ProfileCard.NameAndHandle 53 + profile={profile} 54 + moderationOpts={moderationOpts} 55 + /> 56 + </ProfileCard.Header> 57 + </ProfileCard.Outer> 58 + </View> 59 + )} 60 + </Link> 61 + ) 62 + }
+11 -81
src/view/shell/desktop/Search.tsx
··· 4 4 StyleSheet, 5 5 TouchableOpacity, 6 6 View, 7 - ViewStyle, 7 + type ViewStyle, 8 8 } from 'react-native' 9 - import { 10 - AppBskyActorDefs, 11 - moderateProfile, 12 - ModerationDecision, 13 - } from '@atproto/api' 14 9 import {msg} from '@lingui/macro' 15 10 import {useLingui} from '@lingui/react' 16 11 import {StackActions, useNavigation} from '@react-navigation/native' 17 - import {useQueryClient} from '@tanstack/react-query' 18 12 19 13 import {usePalette} from '#/lib/hooks/usePalette' 20 - import {makeProfileLink} from '#/lib/routes/links' 21 - import {NavigationProp} from '#/lib/routes/types' 22 - import {sanitizeDisplayName} from '#/lib/strings/display-names' 23 - import {sanitizeHandle} from '#/lib/strings/handles' 24 - import {s} from '#/lib/styles' 14 + import {type NavigationProp} from '#/lib/routes/types' 25 15 import {useModerationOpts} from '#/state/preferences/moderation-opts' 26 16 import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' 27 - import {precacheProfile} from '#/state/queries/profile' 28 17 import {Link} from '#/view/com/util/Link' 29 18 import {Text} from '#/view/com/util/text/Text' 30 - import {UserAvatar} from '#/view/com/util/UserAvatar' 19 + import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard' 31 20 import {atoms as a} from '#/alf' 32 21 import {SearchInput} from '#/components/forms/SearchInput' 33 22 ··· 82 71 SearchLinkCard = React.memo(SearchLinkCard) 83 72 export {SearchLinkCard} 84 73 85 - let SearchProfileCard = ({ 86 - profile, 87 - moderation, 88 - onPress: onPressInner, 89 - }: { 90 - profile: AppBskyActorDefs.ProfileViewBasic 91 - moderation: ModerationDecision 92 - onPress: () => void 93 - }): React.ReactNode => { 94 - const pal = usePalette('default') 95 - const queryClient = useQueryClient() 96 - 97 - const onPress = React.useCallback(() => { 98 - precacheProfile(queryClient, profile) 99 - onPressInner() 100 - }, [queryClient, profile, onPressInner]) 101 - 102 - return ( 103 - <Link 104 - testID={`searchAutoCompleteResult-${profile.handle}`} 105 - href={makeProfileLink(profile)} 106 - title={profile.handle} 107 - asAnchor 108 - anchorNoUnderline 109 - onBeforePress={onPress}> 110 - <View 111 - style={[ 112 - pal.border, 113 - { 114 - flexDirection: 'row', 115 - alignItems: 'center', 116 - gap: 12, 117 - paddingVertical: 8, 118 - paddingHorizontal: 12, 119 - }, 120 - ]}> 121 - <UserAvatar 122 - size={40} 123 - avatar={profile.avatar} 124 - moderation={moderation.ui('avatar')} 125 - type={profile.associated?.labeler ? 'labeler' : 'user'} 126 - /> 127 - <View style={{flex: 1}}> 128 - <Text 129 - emoji 130 - type="lg" 131 - style={[s.bold, pal.text, a.self_start]} 132 - numberOfLines={1} 133 - lineHeight={1.2}> 134 - {sanitizeDisplayName( 135 - profile.displayName || sanitizeHandle(profile.handle), 136 - moderation.ui('displayName'), 137 - )} 138 - </Text> 139 - <Text type="md" style={[pal.textLight]} numberOfLines={1}> 140 - {sanitizeHandle(profile.handle, '@')} 141 - </Text> 142 - </View> 143 - </View> 144 - </Link> 145 - ) 146 - } 147 - SearchProfileCard = React.memo(SearchProfileCard) 148 - export {SearchProfileCard} 149 - 150 74 export function DesktopSearch() { 151 75 const {_} = useLingui() 152 76 const pal = usePalette('default') ··· 190 114 onSubmitEditing={onSubmit} 191 115 /> 192 116 {query !== '' && isActive && moderationOpts && ( 193 - <View style={[pal.view, pal.borderDark, styles.resultsContainer]}> 117 + <View 118 + style={[ 119 + pal.view, 120 + pal.borderDark, 121 + styles.resultsContainer, 122 + a.overflow_hidden, 123 + ]}> 194 124 {isFetching && !autocompleteData?.length ? ( 195 125 <View style={{padding: 8}}> 196 126 <ActivityIndicator /> ··· 210 140 <SearchProfileCard 211 141 key={item.did} 212 142 profile={item} 213 - moderation={moderateProfile(item, moderationOpts)} 143 + moderationOpts={moderationOpts} 214 144 onPress={onSearchProfileCardPress} 215 145 /> 216 146 ))}