Files
bsky-social-app/src/view/com/profile/ProfileSubpageHeader.tsx
T
2026-09-03 12:26:51 -07:00

185 lines
5.7 KiB
TypeScript

import {useCallback} from 'react'
import {Pressable, View} from 'react-native'
import Animated, {useAnimatedRef} from 'react-native-reanimated'
import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {makeProfileLink} from '#/lib/routes/links'
import {type NavigationProp} from '#/lib/routes/types'
import {sanitizeHandle} from '#/lib/strings/handles'
import {emitSoftReset} from '#/state/events'
import {TextLink} from '#/view/com/util/Link'
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {Text} from '#/view/com/util/text/Text'
import {UserAvatar, type UserAvatarType} from '#/view/com/util/UserAvatar'
import {StarterPackMultiPathLarge as StarterPackIcon} from '#/components/icons/StarterPack'
import * as Layout from '#/components/Layout'
import {useLightboxControls} from '#/components/Lightbox/state'
import {type app} from '#/lexicons'
export function ProfileSubpageHeader({
isLoading,
href,
title,
avatar,
isOwner,
purpose,
creator,
avatarType,
children,
}: React.PropsWithChildren<{
isLoading?: boolean
href: string
title: string | undefined
avatar: string | undefined
isOwner: boolean | undefined
purpose: app.bsky.graph.defs.ListPurpose | undefined
creator:
| {
did: string
handle: string
}
| undefined
avatarType: UserAvatarType | 'starter-pack'
}>) {
const navigation = useNavigation<NavigationProp>()
const {t: l} = useLingui()
const {isMobile} = useWebMediaQueries()
const {openLightbox} = useLightboxControls()
const pal = usePalette('default')
const canGoBack = navigation.canGoBack()
const aviRef = useAnimatedRef()
const onPressAvi = useCallback(() => {
if (
avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride)
) {
openLightbox({
images: [
{
uri: avatar,
thumbUri: avatar,
thumbRect: null,
thumbRef: aviRef,
dimensions: {
// It's fine if it's actually smaller but we know it's 1:1.
height: 1000,
width: 1000,
},
thumbDimensions: null,
type: 'rect-avi',
},
],
index: 0,
})
}
}, [openLightbox, avatar, aviRef])
return (
<>
<Layout.Header.Outer>
{canGoBack ? (
<Layout.Header.BackButton />
) : (
<Layout.Header.MenuButton />
)}
<Layout.Header.Content />
{children}
</Layout.Header.Outer>
<View
style={{
flexDirection: 'row',
alignItems: 'flex-start',
gap: 10,
paddingTop: 14,
paddingBottom: 14,
paddingHorizontal: isMobile ? 12 : 14,
}}>
<Animated.View ref={aviRef} collapsable={false}>
<Pressable
testID="headerAviButton"
onPress={onPressAvi}
accessibilityRole="image"
accessibilityLabel={l`View the avatar`}
accessibilityHint=""
style={{width: 58}}>
{avatarType === 'starter-pack' ? (
<StarterPackIcon width={58} gradient="sky" />
) : (
<UserAvatar type={avatarType} size={58} avatar={avatar} />
)}
</Pressable>
</Animated.View>
<View style={{flex: 1, gap: 4}}>
{isLoading ? (
<LoadingPlaceholder
width={200}
height={32}
style={{marginVertical: 6}}
/>
) : (
<TextLink
testID="headerTitle"
type="title-xl"
href={href}
style={[pal.text, {fontWeight: '600'}]}
text={title || ''}
onPress={emitSoftReset}
numberOfLines={4}
/>
)}
{isLoading || !creator ? (
<LoadingPlaceholder width={50} height={8} />
) : (
<Text type="lg" style={[pal.textLight]} numberOfLines={1}>
{purpose === 'app.bsky.graph.defs#curatelist' ? (
isOwner ? (
<Trans>List by you</Trans>
) : (
<Trans>
List by{' '}
<TextLink
text={sanitizeHandle(creator.handle || '', '@')}
href={makeProfileLink(creator)}
style={pal.textLight}
/>
</Trans>
)
) : purpose === 'app.bsky.graph.defs#modlist' ? (
isOwner ? (
<Trans>Moderation list by you</Trans>
) : (
<Trans>
Moderation list by{' '}
<TextLink
text={sanitizeHandle(creator.handle || '', '@')}
href={makeProfileLink(creator)}
style={pal.textLight}
/>
</Trans>
)
) : purpose === 'app.bsky.graph.defs#referencelist' ? (
isOwner ? (
<Trans>Starter Pack by you</Trans>
) : (
<Trans>
Starter Pack by{' '}
<TextLink
text={sanitizeHandle(creator.handle || '', '@')}
href={makeProfileLink(creator)}
style={pal.textLight}
/>
</Trans>
)
) : null}
</Text>
)}
</View>
</View>
</>
)
}