ALF search cards (#8210)

This commit is contained in:
Eric Bailey
2025-04-16 18:42:41 -05:00
committed by GitHub
parent fb8ab9f26d
commit faab49bd9e
3 changed files with 78 additions and 85 deletions
@@ -1,12 +1,13 @@
import {memo} from 'react'
import {ActivityIndicator, View} from 'react-native'
import {type AppBskyActorDefs, moderateProfile} from '@atproto/api'
import {type AppBskyActorDefs} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search'
import {SearchLinkCard} from '#/view/shell/desktop/Search'
import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard'
import {atoms as a, native} from '#/alf'
import * as Layout from '#/components/Layout'
@@ -25,8 +26,8 @@ let AutocompleteResults = ({
onResultPress: () => void
onProfileClick: (profile: AppBskyActorDefs.ProfileViewBasic) => void
}): React.ReactNode => {
const moderationOpts = useModerationOpts()
const {_} = useLingui()
const moderationOpts = useModerationOpts()
return (
<>
{(isAutocompleteFetching && !autocompleteData?.length) ||
@@ -54,7 +55,7 @@ let AutocompleteResults = ({
<SearchProfileCard
key={item.did}
profile={item}
moderation={moderateProfile(item, moderationOpts)}
moderationOpts={moderationOpts}
onPress={() => {
onProfileClick(item)
onResultPress()
@@ -0,0 +1,62 @@
import {useCallback} from 'react'
import {View} from 'react-native'
import {type AppBskyActorDefs, type ModerationOpts} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query'
import {makeProfileLink} from '#/lib/routes/links'
import {unstableCacheProfileView} from '#/state/queries/unstable-profile-cache'
import {atoms as a, useTheme} from '#/alf'
import {Link} from '#/components/Link'
import * as ProfileCard from '#/components/ProfileCard'
export function SearchProfileCard({
profile,
moderationOpts,
onPress: onPressInner,
}: {
profile: AppBskyActorDefs.ProfileViewBasic
moderationOpts: ModerationOpts
onPress?: () => void
}) {
const t = useTheme()
const {_} = useLingui()
const qc = useQueryClient()
const onPress = useCallback(() => {
unstableCacheProfileView(qc, profile)
onPressInner?.()
}, [qc, profile, onPressInner])
return (
<Link
testID={`searchAutoCompleteResult-${profile.handle}`}
to={makeProfileLink(profile)}
label={_(msg`View ${profile.handle}'s profile`)}
onPress={onPress}>
{({hovered, pressed}) => (
<View
style={[
a.flex_1,
a.px_md,
a.py_sm,
(hovered || pressed) && t.atoms.bg_contrast_25,
]}>
<ProfileCard.Outer>
<ProfileCard.Header>
<ProfileCard.Avatar
profile={profile}
moderationOpts={moderationOpts}
/>
<ProfileCard.NameAndHandle
profile={profile}
moderationOpts={moderationOpts}
/>
</ProfileCard.Header>
</ProfileCard.Outer>
</View>
)}
</Link>
)
}
+11 -81
View File
@@ -4,30 +4,19 @@ import {
StyleSheet,
TouchableOpacity,
View,
ViewStyle,
type ViewStyle,
} from 'react-native'
import {
AppBskyActorDefs,
moderateProfile,
ModerationDecision,
} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {StackActions, useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
import {usePalette} from '#/lib/hooks/usePalette'
import {makeProfileLink} from '#/lib/routes/links'
import {NavigationProp} from '#/lib/routes/types'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {s} from '#/lib/styles'
import {type NavigationProp} from '#/lib/routes/types'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
import {precacheProfile} from '#/state/queries/profile'
import {Link} from '#/view/com/util/Link'
import {Text} from '#/view/com/util/text/Text'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard'
import {atoms as a} from '#/alf'
import {SearchInput} from '#/components/forms/SearchInput'
@@ -82,71 +71,6 @@ let SearchLinkCard = ({
SearchLinkCard = React.memo(SearchLinkCard)
export {SearchLinkCard}
let SearchProfileCard = ({
profile,
moderation,
onPress: onPressInner,
}: {
profile: AppBskyActorDefs.ProfileViewBasic
moderation: ModerationDecision
onPress: () => void
}): React.ReactNode => {
const pal = usePalette('default')
const queryClient = useQueryClient()
const onPress = React.useCallback(() => {
precacheProfile(queryClient, profile)
onPressInner()
}, [queryClient, profile, onPressInner])
return (
<Link
testID={`searchAutoCompleteResult-${profile.handle}`}
href={makeProfileLink(profile)}
title={profile.handle}
asAnchor
anchorNoUnderline
onBeforePress={onPress}>
<View
style={[
pal.border,
{
flexDirection: 'row',
alignItems: 'center',
gap: 12,
paddingVertical: 8,
paddingHorizontal: 12,
},
]}>
<UserAvatar
size={40}
avatar={profile.avatar}
moderation={moderation.ui('avatar')}
type={profile.associated?.labeler ? 'labeler' : 'user'}
/>
<View style={{flex: 1}}>
<Text
emoji
type="lg"
style={[s.bold, pal.text, a.self_start]}
numberOfLines={1}
lineHeight={1.2}>
{sanitizeDisplayName(
profile.displayName || sanitizeHandle(profile.handle),
moderation.ui('displayName'),
)}
</Text>
<Text type="md" style={[pal.textLight]} numberOfLines={1}>
{sanitizeHandle(profile.handle, '@')}
</Text>
</View>
</View>
</Link>
)
}
SearchProfileCard = React.memo(SearchProfileCard)
export {SearchProfileCard}
export function DesktopSearch() {
const {_} = useLingui()
const pal = usePalette('default')
@@ -190,7 +114,13 @@ export function DesktopSearch() {
onSubmitEditing={onSubmit}
/>
{query !== '' && isActive && moderationOpts && (
<View style={[pal.view, pal.borderDark, styles.resultsContainer]}>
<View
style={[
pal.view,
pal.borderDark,
styles.resultsContainer,
a.overflow_hidden,
]}>
{isFetching && !autocompleteData?.length ? (
<View style={{padding: 8}}>
<ActivityIndicator />
@@ -210,7 +140,7 @@ export function DesktopSearch() {
<SearchProfileCard
key={item.did}
profile={item}
moderation={moderateProfile(item, moderationOpts)}
moderationOpts={moderationOpts}
onPress={onSearchProfileCardPress}
/>
))}